mirror of https://codeberg.org/topola/topola.git
109 lines
4.0 KiB
Rust
109 lines
4.0 KiB
Rust
use geo::{CoordsIter, Point, Polygon};
|
|
use topola::{
|
|
geometry::primitive::{PrimitiveShape, PrimitiveShapeTrait},
|
|
math::{self, Circle},
|
|
};
|
|
|
|
pub struct Painter<'a> {
|
|
ui: &'a mut egui::Ui,
|
|
transform: egui::emath::RectTransform,
|
|
}
|
|
|
|
impl<'a> Painter<'a> {
|
|
pub fn new(ui: &'a mut egui::Ui, transform: egui::emath::RectTransform) -> Self {
|
|
Self { ui, transform }
|
|
}
|
|
|
|
pub fn paint_primitive(&mut self, shape: &PrimitiveShape, color: egui::epaint::Color32) {
|
|
let epaint_shape = match shape {
|
|
PrimitiveShape::Dot(dot) => self.dot_shape(dot.circle, color),
|
|
PrimitiveShape::Seg(seg) => egui::Shape::line_segment(
|
|
[
|
|
self.transform
|
|
.transform_pos([seg.from.x() as f32, -seg.from.y() as f32].into()),
|
|
self.transform
|
|
.transform_pos([seg.to.x() as f32, -seg.to.y() as f32].into()),
|
|
],
|
|
egui::Stroke::new(seg.width as f32 * self.transform.scale().x, color),
|
|
),
|
|
PrimitiveShape::Bend(bend) => {
|
|
let circle = bend.circle();
|
|
let delta_from = bend.from - circle.pos;
|
|
let delta_to = bend.to - circle.pos;
|
|
|
|
let angle_from = delta_from.y().atan2(delta_from.x());
|
|
|
|
let cross = math::cross_product(delta_from, delta_to);
|
|
let dot = math::dot_product(delta_from, delta_to);
|
|
let angle_step = cross.atan2(dot) / 100.0;
|
|
|
|
let mut points: Vec<egui::Pos2> = vec![];
|
|
|
|
for i in 0..=100 {
|
|
let x = circle.pos.x() + circle.r * (angle_from + i as f64 * angle_step).cos();
|
|
let y = circle.pos.y() + circle.r * (angle_from + i as f64 * angle_step).sin();
|
|
points.push(self.transform.transform_pos([x as f32, -y as f32].into()));
|
|
}
|
|
|
|
egui::Shape::line(
|
|
points,
|
|
egui::Stroke::new(bend.width as f32 * self.transform.scale().x, color),
|
|
)
|
|
}
|
|
};
|
|
|
|
self.ui.painter().add(epaint_shape);
|
|
|
|
let envelope = PrimitiveShapeTrait::envelope(shape, 0.0);
|
|
let rect = egui::epaint::Rect {
|
|
min: [envelope.lower()[0] as f32, -envelope.upper()[1] as f32].into(),
|
|
max: [envelope.upper()[0] as f32, -envelope.lower()[1] as f32].into(),
|
|
};
|
|
self.ui.painter().add(egui::Shape::rect_stroke(
|
|
self.transform.transform_rect(rect),
|
|
egui::Rounding::ZERO,
|
|
egui::Stroke::new(1.0, egui::Color32::GRAY),
|
|
));
|
|
}
|
|
|
|
pub fn paint_dot(&mut self, circle: Circle, color: egui::epaint::Color32) {
|
|
let shape = self.dot_shape(circle, color);
|
|
self.ui.painter().add(shape);
|
|
}
|
|
|
|
fn dot_shape(&mut self, circle: Circle, color: egui::epaint::Color32) -> egui::Shape {
|
|
egui::Shape::circle_filled(
|
|
self.transform
|
|
.transform_pos([circle.pos.x() as f32, -circle.pos.y() as f32].into()),
|
|
circle.r as f32 * self.transform.scale().x,
|
|
color,
|
|
)
|
|
}
|
|
|
|
pub fn paint_polygon(&mut self, polygon: &Polygon, color: egui::epaint::Color32) {
|
|
self.ui.painter().add(egui::Shape::convex_polygon(
|
|
polygon
|
|
.exterior_coords_iter()
|
|
.map(|coords| {
|
|
self.transform
|
|
.transform_pos([coords.x as f32, -coords.y as f32].into())
|
|
})
|
|
.collect(),
|
|
color,
|
|
egui::Stroke::default(),
|
|
));
|
|
}
|
|
|
|
pub fn paint_edge(&mut self, from: Point, to: Point, stroke: egui::Stroke) {
|
|
self.ui.painter().add(egui::Shape::line_segment(
|
|
[
|
|
self.transform
|
|
.transform_pos([from.x() as f32, -from.y() as f32].into()),
|
|
self.transform
|
|
.transform_pos([to.x() as f32, -to.y() as f32].into()),
|
|
],
|
|
stroke,
|
|
));
|
|
}
|
|
}
|