pub struct Primitives2D<'a> {
pixels: &'a mut [u8],
width: u32,
height: u32,
}
impl<'a> Primitives2D<'a> {
pub fn new(pixels: &'a mut [u8], width: u32, height: u32) -> Self {
Self { pixels, width, height }
}
pub fn draw_pixel(&mut self, x: i32, y: i32, color: [u8; 4]) {
if x < 0 || x >= self.width as i32 || y < 0 || y >= self.height as i32 {
return;
}
let index = ((y as u32 * self.width + x as u32) * 4) as usize;
if index + 3 < self.pixels.len() {
self.pixels[index..index + 4].copy_from_slice(&color);
}
}
pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: [u8; 4]) {
let dx = (x1 - x0).abs();
let dy = (y1 - y0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx - dy;
let mut x = x0;
let mut y = y0;
loop {
self.draw_pixel(x, y, color);
if x == x1 && y == y1 {
break;
}
let e2 = 2 * err;
if e2 > -dy {
err -= dy;
x += sx;
}
if e2 < dx {
err += dx;
y += sy;
}
}
}
pub fn draw_rect_filled(&mut self, x: i32, y: i32, width: u32, height: u32, color: [u8; 4]) {
for dy in 0..height as i32 {
for dx in 0..width as i32 {
self.draw_pixel(x + dx, y + dy, color);
}
}
}
pub fn draw_rect(&mut self, x: i32, y: i32, width: u32, height: u32, color: [u8; 4]) {
for dx in 0..width as i32 {
self.draw_pixel(x + dx, y, color);
self.draw_pixel(x + dx, y + height as i32 - 1, color);
}
for dy in 0..height as i32 {
self.draw_pixel(x, y + dy, color);
self.draw_pixel(x + width as i32 - 1, y + dy, color);
}
}
pub fn draw_circle(&mut self, cx: i32, cy: i32, radius: i32, color: [u8; 4]) {
let mut x = 0;
let mut y = radius;
let mut d = 3 - 2 * radius;
while x <= y {
self.draw_pixel(cx + x, cy + y, color);
self.draw_pixel(cx - x, cy + y, color);
self.draw_pixel(cx + x, cy - y, color);
self.draw_pixel(cx - x, cy - y, color);
self.draw_pixel(cx + y, cy + x, color);
self.draw_pixel(cx - y, cy + x, color);
self.draw_pixel(cx + y, cy - x, color);
self.draw_pixel(cx - y, cy - x, color);
if d < 0 {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y -= 1;
}
x += 1;
}
}
pub fn draw_circle_filled(&mut self, cx: i32, cy: i32, radius: i32, color: [u8; 4]) {
for y in -radius..=radius {
for x in -radius..=radius {
if x * x + y * y <= radius * radius {
self.draw_pixel(cx + x, cy + y, color);
}
}
}
}
pub fn draw_ellipse(&mut self, cx: i32, cy: i32, rx: i32, ry: i32, color: [u8; 4]) {
let mut x = 0;
let mut y = ry;
let rx2 = rx * rx;
let ry2 = ry * ry;
let mut p = ry2 - (rx2 * ry) + (rx2 / 4);
while (ry2 * x) <= (rx2 * y) {
self.draw_pixel(cx + x, cy + y, color);
self.draw_pixel(cx - x, cy + y, color);
self.draw_pixel(cx + x, cy - y, color);
self.draw_pixel(cx - x, cy - y, color);
if p < 0 {
x += 1;
p = p + (2 * ry2 * x) + ry2;
} else {
x += 1;
y -= 1;
p = p + (2 * ry2 * x) - (2 * rx2 * y) + ry2;
}
}
}
pub fn draw_triangle_filled(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32, color: [u8; 4]) {
let mut points = [(x0, y0), (x1, y1), (x2, y2)];
points.sort_by_key(|p| p.1);
let (x0, y0) = points[0];
let (x1, y1) = points[1];
let (x2, y2) = points[2];
for y in y0..=y2 {
let mut x_start = x0;
let mut x_end = x0;
if y <= y1 {
if y1 != y0 {
x_start = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
}
if y2 != y0 {
x_end = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
}
} else {
if y2 != y1 {
x_start = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
}
if y2 != y0 {
x_end = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
}
}
if x_start > x_end {
std::mem::swap(&mut x_start, &mut x_end);
}
for x in x_start..=x_end {
self.draw_pixel(x, y, color);
}
}
}
pub fn draw_triangle(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32, color: [u8; 4]) {
self.draw_line(x0, y0, x1, y1, color);
self.draw_line(x1, y1, x2, y2, color);
self.draw_line(x2, y2, x0, y0, color);
}
pub fn draw_polygon(&mut self, points: &[(i32, i32)], color: [u8; 4]) {
if points.len() < 2 {
return;
}
for i in 0..points.len() {
let (x0, y0) = points[i];
let (x1, y1) = points[(i + 1) % points.len()];
self.draw_line(x0, y0, x1, y1, color);
}
}
pub fn draw_thick_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, thickness: i32, color: [u8; 4]) {
for t in -thickness/2..=thickness/2 {
let dx = (y1 - y0) as f32;
let dy = (x1 - x0) as f32;
let len = (dx * dx + dy * dy).sqrt();
if len > 0.0 {
let offset_x = (t as f32 * dx / len) as i32;
let offset_y = (t as f32 * dy / len) as i32;
self.draw_line(x0 + offset_x, y0 - offset_y, x1 + offset_x, y1 - offset_y, color);
}
}
}
pub fn draw_arc(&mut self, cx: i32, cy: i32, radius: i32, start_angle: f32, end_angle: f32, color: [u8; 4]) {
let steps = (radius as f32 * (end_angle - start_angle).abs()) as i32;
for i in 0..=steps {
let angle = start_angle + (end_angle - start_angle) * (i as f32 / steps as f32);
let x = cx + (angle.cos() * radius as f32) as i32;
let y = cy + (angle.sin() * radius as f32) as i32;
self.draw_pixel(x, y, color);
}
}
pub fn draw_grid(&mut self, cell_size: u32, color: [u8; 4]) {
for x in (0..self.width).step_by(cell_size as usize) {
for y in 0..self.height {
self.draw_pixel(x as i32, y as i32, color);
}
}
for y in (0..self.height).step_by(cell_size as usize) {
for x in 0..self.width {
self.draw_pixel(x as i32, y as i32, color);
}
}
}
pub fn flood_fill(&mut self, x: i32, y: i32, target_color: [u8; 4], fill_color: [u8; 4]) {
if x < 0 || x >= self.width as i32 || y < 0 || y >= self.height as i32 {
return;
}
let index = ((y as u32 * self.width + x as u32) * 4) as usize;
if index + 3 >= self.pixels.len() {
return;
}
let current_color = [
self.pixels[index],
self.pixels[index + 1],
self.pixels[index + 2],
self.pixels[index + 3],
];
if current_color != target_color || current_color == fill_color {
return;
}
let mut stack = vec![(x, y)];
while let Some((px, py)) = stack.pop() {
if px < 0 || px >= self.width as i32 || py < 0 || py >= self.height as i32 {
continue;
}
let idx = ((py as u32 * self.width + px as u32) * 4) as usize;
if idx + 3 >= self.pixels.len() {
continue;
}
let pixel_color = [
self.pixels[idx],
self.pixels[idx + 1],
self.pixels[idx + 2],
self.pixels[idx + 3],
];
if pixel_color == target_color {
self.draw_pixel(px, py, fill_color);
stack.push((px + 1, py));
stack.push((px - 1, py));
stack.push((px, py + 1));
stack.push((px, py - 1));
}
}
}
pub fn draw_gradient_h(&mut self, x: i32, y: i32, width: u32, height: u32, color1: [u8; 4], color2: [u8; 4]) {
for dx in 0..width {
let t = dx as f32 / width as f32;
let r = (color1[0] as f32 * (1.0 - t) + color2[0] as f32 * t) as u8;
let g = (color1[1] as f32 * (1.0 - t) + color2[1] as f32 * t) as u8;
let b = (color1[2] as f32 * (1.0 - t) + color2[2] as f32 * t) as u8;
let a = (color1[3] as f32 * (1.0 - t) + color2[3] as f32 * t) as u8;
let color = [r, g, b, a];
for dy in 0..height {
self.draw_pixel(x + dx as i32, y + dy as i32, color);
}
}
}
pub fn draw_gradient_v(&mut self, x: i32, y: i32, width: u32, height: u32, color1: [u8; 4], color2: [u8; 4]) {
for dy in 0..height {
let t = dy as f32 / height as f32;
let r = (color1[0] as f32 * (1.0 - t) + color2[0] as f32 * t) as u8;
let g = (color1[1] as f32 * (1.0 - t) + color2[1] as f32 * t) as u8;
let b = (color1[2] as f32 * (1.0 - t) + color2[2] as f32 * t) as u8;
let a = (color1[3] as f32 * (1.0 - t) + color2[3] as f32 * t) as u8;
let color = [r, g, b, a];
for dx in 0..width {
self.draw_pixel(x + dx as i32, y + dy as i32, color);
}
}
}
pub fn draw_gradient_radial(&mut self, cx: i32, cy: i32, radius: i32, color1: [u8; 4], color2: [u8; 4]) {
for y in -radius..=radius {
for x in -radius..=radius {
let dist = ((x * x + y * y) as f32).sqrt();
if dist <= radius as f32 {
let t = dist / radius as f32;
let r = (color1[0] as f32 * (1.0 - t) + color2[0] as f32 * t) as u8;
let g = (color1[1] as f32 * (1.0 - t) + color2[1] as f32 * t) as u8;
let b = (color1[2] as f32 * (1.0 - t) + color2[2] as f32 * t) as u8;
let a = (color1[3] as f32 * (1.0 - t) + color2[3] as f32 * t) as u8;
self.draw_pixel(cx + x, cy + y, [r, g, b, a]);
}
}
}
}
}