


// nannou ํ๋ ์์ํฌ์ ๊ฑฐ์ ๋ชจ๋ ๊ธฐ๋ฅ์ ํฌํจํ๋ prelude๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
// ์ฑ ์์ฑ, ๊ทธ๋ฆฌ๊ธฐ, ์์ ๋ฑ ํต์ฌ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ์ ๊ทผํ ์ ์์ต๋๋ค.
use nannou::prelude::*;
// image ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ open ํจ์๋ฅผ ๊ฐ์ ธ์ ์ด๋ฏธ์ง ํ์ผ์ ๋ถ๋ฌ์ฌ ์ ์๊ฒ ํฉ๋๋ค.
use image::open;
// --- ์์ ์ ์ ---
// ์ด๋ฏธ์ง๋ฅผ ์ํ๋งํ ๊ฒฉ์์ ํฌ๊ธฐ๋ฅผ ์ ์ํฉ๋๋ค. 100x100 ๊ฒฉ์๋ฅผ ์๋ฏธํฉ๋๋ค.
const GRID_SIZE: usize = 100;
// ์บ๋ฒ์ค(์ฐฝ)์ ๊ฐ๋ก/์ธ๋ก ํฌ๊ธฐ๋ฅผ ์ ์ํฉ๋๋ค.
const CANVAS_SIZE: f32 = 1080.0;
// --- ๋ฐ์ดํฐ ๋ชจ๋ธ ์ ์ ---
// ์ฑ์ ์ํ(state)๋ฅผ ์ ์ฅํ ๊ตฌ์กฐ์ฒด์
๋๋ค.
// ์ด ์์ ์์๋ ์ด๋ฏธ์ง ๊ฐ ํฝ์
์ ๋ฐ๊ธฐ ๊ฐ์ 0.0 ~ 1.0 ์ฌ์ด๋ก ๋ณํํ์ฌ ์ ์ฅํฉ๋๋ค.
struct Model {
brightness_grid: Vec<f32>,
}
// --- ์ฃผ ์คํ ํจ์ ---
// ํ๋ก๊ทธ๋จ์ ์์์ (entry point)์
๋๋ค.
fn main() {
// nannou ์ฑ์ ์ด๊ธฐํํ๊ณ model ํจ์๋ฅผ ์ค์ ํฉ๋๋ค.
// .update()๋ ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋ ๋ก์ง์, .run()์ ์ฑ์ ์คํ์ํต๋๋ค.
nannou::app(model).update(update).run();
}
// --- ๋ชจ๋ธ ์ด๊ธฐํ ํจ์ ---
// ์ฑ์ด ์ฒ์ ์์๋ ๋ ๋ฑ ํ ๋ฒ ํธ์ถ๋ฉ๋๋ค.
// ์ฑ์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ ํ๊ณ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ์ญํ ์ ํฉ๋๋ค.
fn model(app: &App) -> Model {
// 1. ์ฐฝ(window) ์ค์
// ์ฑ์ ์ฐฝ์ ๋ง๋ค๊ณ , ํฌ๊ธฐ๋ฅผ CANVAS_SIZE๋ก ์ง์ ํ๋ฉฐ, view ํจ์๋ฅผ ๋ ๋๋ง ๋ฃจํ๋ก ์ฐ๊ฒฐํฉ๋๋ค.
// ์ด๋ ๊ฒ model ํจ์์์ ์ฐฝ์ ์์ฑํ๋ฉด ์์ ์ ํฌ๊ธฐ๋ฅผ ๋ช
ํํ๊ฒ ์ ์ดํ ์ ์์ต๋๋ค.
app.new_window()
.size(CANVAS_SIZE as u32, CANVAS_SIZE as u32)
.view(view) // ์ด ์ฐฝ์์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ํจ์๋ก view๋ฅผ ์ง์
.build()
.unwrap();
// 2. ์ด๋ฏธ์ง ํ์ผ ๊ฒฝ๋ก ์ค์ (์ด์ ๊ฐ๋ฅํ ๋ฐฉ์)
// app.assets_path()๋ 'assets' ํด๋์ ๊ฒฝ๋ก๋ฅผ ๋์ ์ผ๋ก ์ฐพ์์ค๋๋ค.
// ์ด๋ฅผ ํตํด ๋ค๋ฅธ ์ปดํจํฐ์์๋ ํ๋ก์ ํธ ๊ตฌ์กฐ๋ง ๊ฐ๋ค๋ฉด ์ฝ๋๋ฅผ ์์ ์์ด ์คํํ ์ ์์ต๋๋ค.
let assets = app.assets_path().expect("assets ํด๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
let img_path = assets.join("input.jpg"); // 'assets/input.jpg' ๊ฒฝ๋ก ์์ฑ
// 3. ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ ๋ฐ ์๋ฌ ์ฒ๋ฆฌ
let img = match open(&img_path) {
Ok(img) => img, // ์ฑ๊ณต ์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ๋ฐํ
Err(e) => {
// ์คํจ ์ ์๋ฌ ๋ฉ์์ง๋ฅผ ํฐ๋ฏธ๋์ ์ถ๋ ฅํ๊ณ ํ๋ก๊ทธ๋จ์ ์ข
๋ฃํฉ๋๋ค.
eprintln!("์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ์ต๋๋ค: {:?}. ๊ฒฝ๋ก: {:?}", e, img_path);
std::process::exit(1);
}
};
// 4. ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ ๋ฐ ํฝ์
๋ฐ์ดํฐ ์ถ์ถ
// ์ด๋ฏธ์ง๋ฅผ GRID_SIZE x GRID_SIZE ํฌ๊ธฐ๋ก ๋ฆฌ์ฌ์ด์ฆํฉ๋๋ค.
// Lanczos3 ํํฐ๋ ํ์ง ์ ํ๋ฅผ ์ต์ํํ๋ ๊ณ ํ์ง ๋ฆฌ์ํ๋ง ์๊ณ ๋ฆฌ์ฆ์
๋๋ค.
// to_rgba8()์ ํตํด ๊ฐ ํฝ์
์ R, G, B, Alpha(ํฌ๋ช
๋) ์ฑ๋๋ก ๋ณํํฉ๋๋ค.
let resized = img
.resize_to_fill(
GRID_SIZE as u32,
GRID_SIZE as u32,
image::imageops::FilterType::Lanczos3,
)
.to_rgba8();
// 5. ๋ฐ๊ธฐ ๊ฐ ๊ณ์ฐ ๋ฐ ์ ์ฅ
// GRID_SIZE * GRID_SIZE ๋งํผ์ ๊ณต๊ฐ์ ๋ฏธ๋ฆฌ ํ ๋นํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํฉ๋๋ค.
let mut brightness_grid = Vec::with_capacity(GRID_SIZE * GRID_SIZE);
// ๋ฆฌ์ฌ์ด์ฆ๋ ์ด๋ฏธ์ง์ ๋ชจ๋ ํฝ์
์ ์ํํฉ๋๋ค.
for pixel in resized.pixels() {
// ๊ฐ ํฝ์
์ R, G, B ๊ฐ์ f32 ํ์
์ผ๋ก ๊ฐ์ ธ์ต๋๋ค.
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
// ๊ฐ๋จํ ํ๊ท ๋ฒ์ผ๋ก ํ๋ฐฑ(grayscale) ๋ฐ๊ธฐ ๊ฐ์ ๊ณ์ฐํฉ๋๋ค. (R+G+B)/3
// 255.0์ผ๋ก ๋๋์ด ๊ฐ์ 0.0 ~ 1.0 ๋ฒ์๋ก ์ ๊ทํ(normalize)ํฉ๋๋ค.
let gray = (r + g + b) / 3.0 / 255.0;
brightness_grid.push(gray);
}
// ๊ณ์ฐ๋ ๋ฐ๊ธฐ ๊ฐ๋ค์ด ๋ด๊ธด grid๋ฅผ Model ๊ตฌ์กฐ์ฒด์ ๋ด์ ๋ฐํํฉ๋๋ค.
Model { brightness_grid }
}
// --- ์
๋ฐ์ดํธ ํจ์ ---
// ๋งค ํ๋ ์๋ง๋ค view ํจ์๊ฐ ํธ์ถ๋๊ธฐ ์ง์ ์ ํธ์ถ๋ฉ๋๋ค.
// ๋ชจ๋ธ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ ๋๋ฉ์ด์
๋ก์ง์ ๋ฃ์ ๋ ์ฌ์ฉํฉ๋๋ค.
// ์ด ์์ ๋ ์ ์ ์ด๋ฏ๋ก ์๋ฌด๋ฐ ์์
์ ํ์ง ์์ต๋๋ค.
fn update(_app: &App, _model: &mut Model, _update: Update) {}
// --- ๋ทฐ(๊ทธ๋ฆฌ๊ธฐ) ํจ์ ---
// ๋งค ํ๋ ์๋ง๋ค ํ๋ฉด์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆฌ๋ ์ญํ ์ ํฉ๋๋ค.
fn view(app: &App, model: &Model, frame: Frame) {
// 'draw' ์ธ์คํด์ค๋ ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ์ ์๋ ๋๊ตฌ๋ค์ ์ ๊ณตํฉ๋๋ค.
let draw = app.draw();
// ๋ฐฐ๊ฒฝ์ ๊ฒ์์์ผ๋ก ์น ํฉ๋๋ค.
draw.background().color(BLACK);
// ๊ทธ๋ฆฌ๋์ ํ ์นธ(cell)์ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํฉ๋๋ค.
let cell_size = CANVAS_SIZE / GRID_SIZE as f32;
// ์์ด ๊ทธ๋ ค์ง ์ต๋ ๋ฐ์ง๋ฆ์ ๊ณ์ฐํฉ๋๋ค. (์
ํฌ๊ธฐ์ ์ ๋ฐ)
let max_radius = cell_size / 2.0;
// ์ ์ฅ๋ ๋ชจ๋ ๋ฐ๊ธฐ ๊ฐ์ ์ํํ๋ฉด์ ์ธ๋ฑ์ค(i)์ ๋ฐ๊ธฐ ๊ฐ(brightness)์ ํจ๊ป ๊ฐ์ ธ์ต๋๋ค.
for (i, &brightness) in model.brightness_grid.iter().enumerate() {
// 1์ฐจ์ ๋ฐฐ์ด์ ์ธ๋ฑ์ค(i)๋ฅผ 2์ฐจ์ ๊ทธ๋ฆฌ๋์ ํ(row)๊ณผ ์ด(col)๋ก ๋ณํํฉ๋๋ค.
let row = i / GRID_SIZE;
let col = i % GRID_SIZE;
// nannou์ ์ขํ๊ณ(-CANVAS_SIZE/2 ~ +CANVAS_SIZE/2)์ ๋ง์ถฐ ์์ ์ค์ฌ (x, y)๋ฅผ ๊ณ์ฐํฉ๋๋ค.
let x = col as f32 * cell_size + cell_size / 2.0 - CANVAS_SIZE / 2.0;
let y = -(row as f32 * cell_size + cell_size / 2.0 - CANVAS_SIZE / 2.0); // y ์ขํ๋ฅผ ๋ค์ง์ด ์ด๋ฏธ์ง์ ์์ชฝ์ด ์บ๋ฒ์ค ์์ชฝ๊ณผ ๋ง๊ฒ ํจ
// ์ด๋ฏธ์ง์ ๋ฐ๊ธฐ(brightness)์ ๋ฐ๋ผ ์์ ๋ฐ์ง๋ฆ(radius)์ ๊ฒฐ์ ํฉ๋๋ค.
// ๋ฐ์์๋ก(1.0) ๋ฐ์ง๋ฆ์ด ์ปค์ง๊ณ , ์ด๋์ธ์๋ก(0.0) ์์์ง๋๋ค.
let radius = brightness * max_radius;
// ๊ณ์ฐ๋ ์์น์ ๋ฐ์ง๋ฆ์ผ๋ก ํฐ์ ์์ ๊ทธ๋ฆฝ๋๋ค.
draw.ellipse()
.x_y(x, y)
.radius(radius)
.color(WHITE);
}
// ์ง๊ธ๊น์ง ๊ทธ๋ฆฐ ๋ชจ๋ ๋ด์ฉ์ ํ๋ ์์ ์ต์ข
์ ์ผ๋ก ๋ ๋๋งํฉ๋๋ค.
draw.to_frame(app, &frame).unwrap();
}
์ค์์์๋ถํฐ ๋ฌผ๊ฒฐ์ด ํผ์ ธ๋๊ฐ๋ ๋ฏํ ํจํด์ ๋ง๋ค๋ ค๋ฉด ์๊ฐ๊ณผ ๊ฐ ์ ์ ์ค์ฌ์ผ๋ก๋ถํฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ํจ๊ป ์ฌ์ฉํฉ๋๋ค.
์๊ฐ (time): update ํจ์์์ ๊ณ์ ์ฆ๊ฐํ๋ ๋ณ์๋ฅผ ๋์ด ์ ๋๋ฉ์ด์
์ ๊ธฐ๋ฐ์ผ๋ก ์ผ์ต๋๋ค.
๊ฑฐ๋ฆฌ (dist): ๊ฐ ์ ์ด ์บ๋ฒ์ค ์ค์(0, 0)์์ ์ผ๋ง๋ ๋จ์ด์ ธ ์๋์ง ๊ณ์ฐํฉ๋๋ค.
์ฌ์ธ ํจ์ (sin)์ ํ์ฉ
sin(์๊ฐ - ๊ฑฐ๋ฆฌ) ์ ๊ฐ์ ํํ๋ก ์ฌ์ธ ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด, ์๊ฐ์ ๋ฐ๋ผ ์ง๋ํ๋ฉด์๋ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ์์(phase)์ด ๋ฌ๋ผ์ง๋ ํจ๊ณผ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ๋ฌผ๊ฒฐ ํจํด์ ์๋ฆฌ์
๋๋ค.
use nannou::prelude::*;
use image::open;
// --- ์์ ์ ์ ---
const GRID_SIZE: usize = 100;
const CANVAS_SIZE: f32 = 1080.0;
// --- ๋ฐ์ดํฐ ๋ชจ๋ธ ์ ์ ---
// ์ฑ์ ์ํ๋ฅผ ์ ์ฅํ ๊ตฌ์กฐ์ฒด์
๋๋ค.
// ์ด๋ฏธ์ง ๋ฐ๊ธฐ ๊ฐ๊ณผ ํจ๊ป ์ ๋๋ฉ์ด์
์ ์ํ 'time' ๋ณ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
struct Model {
brightness_grid: Vec<f32>,
time: f32, // ์๊ฐ์ ํ๋ฆ์ ์ ์ฅํ ๋ณ์
}
// --- ์ฃผ ์คํ ํจ์ ---
fn main() {
nannou::app(model).update(update).run();
}
// --- ๋ชจ๋ธ ์ด๊ธฐํ ํจ์ ---
fn model(app: &App) -> Model {
app.new_window()
.size(CANVAS_SIZE as u32, CANVAS_SIZE as u32)
.view(view)
.build()
.unwrap();
let assets = app.assets_path().expect("assets ํด๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
let img_path = assets.join("input.jpg");
let img = match open(&img_path) {
Ok(img) => img,
Err(e) => {
eprintln!("์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ์ต๋๋ค: {:?}. ๊ฒฝ๋ก: {:?}", e, img_path);
std::process::exit(1);
}
};
let resized = img
.resize_to_fill(
GRID_SIZE as u32,
GRID_SIZE as u32,
image::imageops::FilterType::Lanczos3,
)
.to_rgba8();
let mut brightness_grid = Vec::with_capacity(GRID_SIZE * GRID_SIZE);
for pixel in resized.pixels() {
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
let gray = (r + g + b) / 3.0 / 255.0;
brightness_grid.push(gray);
}
// Model์ ์ด๊ธฐํํ ๋ time ๊ฐ์ 0.0์ผ๋ก ์ค์ ํฉ๋๋ค.
Model {
brightness_grid,
time: 0.0,
}
}
// --- ์
๋ฐ์ดํธ ํจ์ ---
// ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋์ด ๋ชจ๋ธ์ ์ํ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
// ์ด์ ์ด ํจ์๋ ์ ๋๋ฉ์ด์
์ ํต์ฌ ์ญํ ์ ํฉ๋๋ค.
fn update(_app: &App, model: &mut Model, _update: Update) {
// ๋งค ํ๋ ์๋ง๋ค time ๊ฐ์ ์กฐ๊ธ์ฉ ์ฆ๊ฐ์ํต๋๋ค.
// ์ด ๊ฐ์ ์กฐ์ ํ์ฌ ์ ๋๋ฉ์ด์
์ ์ ์ฒด ์๋๋ฅผ ์ ์ดํ ์ ์์ต๋๋ค.
model.time += 0.03;
}
// --- ๋ทฐ(๊ทธ๋ฆฌ๊ธฐ) ํจ์ ---
// ๋งค ํ๋ ์๋ง๋ค ํ๋ฉด์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆฝ๋๋ค.
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(BLACK);
let cell_size = CANVAS_SIZE / GRID_SIZE as f32;
let max_radius = cell_size / 2.0;
for (i, &brightness) in model.brightness_grid.iter().enumerate() {
let row = i / GRID_SIZE;
let col = i % GRID_SIZE;
let x = col as f32 * cell_size + cell_size / 2.0 - CANVAS_SIZE / 2.0;
let y = -(row as f32 * cell_size + cell_size / 2.0 - CANVAS_SIZE / 2.0);
// --- ์ ๋๋ฉ์ด์
๋ก์ง ์์ ---
// 1. ์บ๋ฒ์ค ์ค์ฌ(0,0)์ผ๋ก๋ถํฐ ํ์ฌ ์ ๊น์ง์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํฉ๋๋ค. (ํผํ๊ณ ๋ผ์ค ์ ๋ฆฌ)
let dist = (x.powi(2) + y.powi(2)).sqrt();
// 2. ์๊ฐ๊ณผ ๊ฑฐ๋ฆฌ๋ฅผ ์กฐํฉํ์ฌ ์ฌ์ธ ํจ์์ ์
๋ ฅ๊ฐ(์์, phase)์ ๋ง๋ญ๋๋ค.
// model.time: ์๊ฐ์ด ํ๋ฅด๋ฉด์ ์ ์ฒด์ ์ธ ํ๋์ ๋ง๋ญ๋๋ค.
// dist * 0.02: ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ํ๋์ ์์์ ์ ๋ค๋ฅด๊ฒ ํ์ฌ ๋ฌผ๊ฒฐ ํจ๊ณผ๋ฅผ ์ค๋๋ค.
let phase = model.time - dist * 0.02;
// 3. ์ฌ์ธ ํจ์์ ๊ฒฐ๊ณผ(-1.0 ~ 1.0)๋ฅผ ํฌ๊ธฐ ๋ฐฐ์จ(0.0 ~ 1.0)๋ก ๋ณํํฉ๋๋ค.
// (sin(phase) + 1.0) -> 0.0 ~ 2.0 ๋ฒ์๋ก ๋ณํ
// * 0.5 -> 0.0 ~ 1.0 ๋ฒ์๋ก ์ต์ข
๋ณํ
let scale = (phase.sin() + 1.0) * 0.5;
// 4. ์ด๋ฏธ์ง ๋ฐ๊ธฐ์ ๋ฐ๋ฅธ ๊ธฐ๋ณธ ๋ฐ์ง๋ฆ์ ๊ณ์ฐํฉ๋๋ค.
let base_radius = brightness * max_radius;
// 5. ๊ธฐ๋ณธ ๋ฐ์ง๋ฆ์ ์ ๋๋ฉ์ด์
๋ฐฐ์จ(scale)์ ๊ณฑํ์ฌ ์ต์ข
๋ฐ์ง๋ฆ์ ๊ฒฐ์ ํฉ๋๋ค.
let radius = base_radius * scale;
// --- ์ ๋๋ฉ์ด์
๋ก์ง ๋ ---
draw.ellipse()
.x_y(x, y)
.radius(radius)
.color(WHITE);
}
draw.to_frame(app, &frame).unwrap();
}
Model ๊ตฌ์กฐ์ฒดtime: f32 ํ๋๊ฐ ์ถ๊ฐ๋์ด ์ฑ ์ ์ญ์์ ์๊ฐ ๊ฐ์ ๊ณต์ ํฉ๋๋ค.update ํจ์model.time ๊ฐ์ ๋งค ํ๋ ์ ์ฆ๊ฐ์์ผ ์ ๋๋ฉ์ด์
์ ๊ตฌ๋ํฉ๋๋ค.view ํจ์ ๋ด๋ถ(x, y)๋ฅผ ์ด์ฉํด ์ค์ฌ์ผ๋ก๋ถํฐ์ ๊ฑฐ๋ฆฌ dist๋ฅผ ๊ณ์ฐํฉ๋๋ค.model.time๊ณผ dist๋ฅผ ์กฐํฉํด ์ฌ์ธํ์ phase๋ฅผ ๋ง๋ญ๋๋ค.0.0 ~ 1.0 ์ฌ์ด์ scale ๊ฐ์ผ๋ก ๋ณํํฉ๋๋ค.brightness * max_radius์ ์ด scale ๊ฐ์ ๊ณฑํ์ฌ ์ต์ข
radius๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.๐ ๏ธ ์ ๋๋ฉ์ด์
์กฐ์ ํ๊ธฐ
์ฝ๋๋ฅผ ์ง์ ์์ ํ๋ฉฐ ๋ค๋ฅธ ๋๋์ ํจํด์ ๋ง๋ค์ด๋ณผ ์ ์์ต๋๋ค.
๐ ๏ธ ์๋ ์กฐ์
update ํจ์์ model.time += 0.03; ์์ 0.03์ ๋ ํฐ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ฉด ์ ๋๋ฉ์ด์
์ด ๋นจ๋ผ์ง๊ณ , ์์ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ฉด ๋๋ ค์ง๋๋ค.
๐ ๏ธ ๋ฌผ๊ฒฐ ๊ฐ๊ฒฉ ์กฐ์
view ํจ์์ let phase = model.time - dist * 0.02; ์์ 0.02๋ฅผ ๋ ํฐ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ฌผ๊ฒฐ์ด ๋ ์ด์ดํด์ง๊ณ (ํ์ฅ์ด ์งง์์ง๊ณ ), ์์ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ ๋๊ฒ ํผ์ง๋๋ค.
โ โ๏ธ๐ ๏ธ๐๐๐จ๐ผ๏ธ๐งฉ๐ง ๐งฎ๐น๏ธ๐งฟโณ๐๏ธ๐ฎ๐๐ชฉ๐๏ธ๐๐งฐ๐
์๋ณด์นด๋
Sample
Important
Highlighting
๋น์
์ ํฉ์
purple
hot pink
yellow
์ด๊ตฌ์
orange
green
blue