use num_traits::cast;
use resvg::usvg::{NodeExt, TreeWriting, XmlOptions};
use resvg::{tiny_skia, usvg};
use std::rc::Rc;
pub use resvg::usvg::Color;
pub static COLORS: [Color; 16] = [
Color {
red: 0,
green: 0,
blue: 0,
},
Color {
red: 0,
green: 0,
blue: 255,
},
Color {
red: 0,
green: 255,
blue: 255,
},
Color {
red: 0,
green: 255,
blue: 0,
},
Color {
red: 255,
green: 0,
blue: 0,
},
Color {
red: 255,
green: 0,
blue: 255,
},
Color {
red: 255,
green: 255,
blue: 0,
},
Color {
red: 255,
green: 255,
blue: 255,
},
Color {
red: 165,
green: 42,
blue: 42,
},
Color {
red: 210,
green: 180,
blue: 140,
},
Color {
red: 34,
green: 139,
blue: 34,
},
Color {
red: 127,
green: 255,
blue: 212,
},
Color {
red: 250,
green: 128,
blue: 114,
},
Color {
red: 128,
green: 0,
blue: 128,
},
Color {
red: 255,
green: 165,
blue: 0,
},
Color {
red: 128,
green: 128,
blue: 128,
},
];
fn u32_to_f32(num: u32) -> f32 {
cast(num).unwrap_or_else(|| panic!("failed to convert u32 '{num}' to f32"))
}
fn f32_to_u32(num: f32) -> u32 {
cast(num.round()).unwrap_or_else(|| panic!("failed to convert f32 '{num}' to u32"))
}
fn i32_to_f32(num: i32) -> f32 {
cast(num).unwrap_or_else(|| panic!("failed to convert i32 '{num}' to f32"))
}
fn f32_to_i32(num: f32) -> i32 {
cast(num.round()).unwrap_or_else(|| panic!("failed to convert f32 '{num}' to i32"))
}
fn normalize_direction(direction: i32) -> i32 {
let normalized = direction % 360;
if normalized < 0 {
normalized + 360
} else {
normalized
}
}
pub fn get_end_coordinates(x: i32, y: i32, direction: i32, length: i32) -> (i32, i32) {
let x = i32_to_f32(x);
let y = i32_to_f32(y);
let length = i32_to_f32(length);
let (end_x, end_y) = get_end_coordinates_precise(x, y, direction, length);
let end_x = f32_to_i32(end_x);
let end_y = f32_to_i32(end_y);
(end_x, end_y)
}
fn get_end_coordinates_precise(x: f32, y: f32, direction: i32, length: f32) -> (f32, f32) {
let x = quantize(x);
let y = quantize(y);
let direction = normalize_direction(direction);
let direction_rad = ((direction as f32) - 90.0).to_radians();
let end_x = quantize(x + (direction_rad.cos() * length));
let end_y = quantize(y + (direction_rad.sin() * length));
(end_x, end_y)
}
#[derive(Clone)]
pub struct Image {
width: u32,
height: u32,
tree: usvg::Tree,
}
fn quantize(x: f32) -> f32 {
(x * 256.0).round() / 256.0
}
impl Image {
pub fn new(width: u32, height: u32) -> Image {
let size = usvg::Size::from_wh(width as f32, height as f32).unwrap();
let tree = usvg::Tree {
size,
view_box: usvg::ViewBox {
rect: size.to_non_zero_rect(0.0, 0.0),
aspect: usvg::AspectRatio::default(),
},
root: usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default())),
};
let fill = usvg::Fill::from_paint(usvg::Paint::Color(usvg::Color::black()));
let mut path = usvg::Path::new(Rc::from(tiny_skia::PathBuilder::from_rect(
size.to_non_zero_rect(0.0, 0.0).to_rect(),
)));
path.fill = Some(fill);
tree.root.append_kind(usvg::NodeKind::Path(path));
Image {
width,
height,
tree,
}
}
pub fn get_dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
let rtree = resvg::Tree::from_usvg(&self.tree);
let pixmap_size = rtree.size.to_int_size();
let mut pixmap = tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
rtree.render(tiny_skia::Transform::default(), &mut pixmap.as_mut());
pixmap.save_png(path).map_err(|e| e.to_string())
}
pub fn save_svg<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
std::fs::write(path, self.tree.to_string(&XmlOptions::default())).map_err(|e| e.to_string())
}
pub fn draw_simple_line(
&mut self,
x: i32,
y: i32,
direction: i32,
length: i32,
color: Color,
) -> Result<(i32, i32), String> {
let (end_x, end_y) = get_end_coordinates(x, y, direction, length);
let paint = usvg::Paint::Color(color);
let mut path = tiny_skia::PathBuilder::new();
path.move_to(i32_to_f32(x), i32_to_f32(y));
path.line_to(i32_to_f32(end_x), i32_to_f32(end_y));
let mut path = usvg::Path::new(
path.finish()
.ok_or("Could not draw line".to_string())?
.into(),
);
let mut stroke = usvg::Stroke::default();
stroke.paint = paint;
path.stroke = Some(stroke);
self.tree.root.append_kind(usvg::NodeKind::Path(path));
Ok((end_x, end_y))
}
}