microcad_export/svg/
canvas.rs1use geo::MultiPolygon;
7use microcad_core::{
8 Bounds2D, Circle, Geometries2D, Geometry2D, Line, LineString, MultiLineString, Point, Polygon,
9 Rect, Scalar, Size2, Vec2, geo2d,
10};
11
12use crate::svg::CenteredText;
13
14#[derive(Clone, Debug)]
16pub struct Canvas {
17 pub rect: geo2d::Rect,
19 pub content_rect: geo2d::Rect,
21 pub size: Size2,
23}
24
25impl Canvas {
26 pub fn new_centered_content(
28 size: Size2,
29 content_rect: geo2d::Rect,
30 scale: Option<Scalar>,
31 ) -> Self {
32 let scale = match scale {
34 Some(scale) => scale,
35 None => {
36 let scale_x = size.width / content_rect.width();
37 let scale_y = size.height / content_rect.height();
38 scale_x.min(scale_y)
39 }
40 };
41
42 let width = content_rect.width() * scale;
44 let height = content_rect.height() * scale;
45
46 let min = Point::new((size.width - width) / 2.0, (size.height - height) / 2.0);
48
49 let rect = geo2d::Rect::new(min, min + geo2d::Point::new(width, height));
51
52 Canvas {
53 rect,
54 content_rect,
55 size,
56 }
57 }
58
59 pub fn scale(&self) -> Scalar {
61 (self.rect.width() / self.content_rect.width())
62 .min(self.rect.height() / self.content_rect.height())
63 }
64}
65
66pub trait MapToCanvas: Sized {
68 fn map_to_canvas(&self, canvas: &Canvas) -> Self;
70}
71
72impl MapToCanvas for Scalar {
74 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
75 self * canvas.scale()
76 }
77}
78
79impl MapToCanvas for (Scalar, Scalar) {
80 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
81 let scale = canvas.scale();
82 let new_width = canvas.rect.width() / scale;
83 let new_height = canvas.rect.height() / scale;
84
85 let x = self.0 - canvas.content_rect.min().x;
86 let y = canvas.content_rect.max().y - self.1; let x = x / new_width * canvas.rect.width() + canvas.rect.min().x;
88 let y = y / new_height * canvas.rect.height() + canvas.rect.min().y;
89 (x, y)
90 }
91}
92
93impl MapToCanvas for Point {
94 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
95 Point::from(self.x_y().map_to_canvas(canvas))
96 }
97}
98
99impl MapToCanvas for Vec2 {
100 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
101 Vec2::from((self.x, self.y).map_to_canvas(canvas))
102 }
103}
104
105impl MapToCanvas for Line {
106 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
107 Self(self.0.map_to_canvas(canvas), self.1.map_to_canvas(canvas))
108 }
109}
110
111impl MapToCanvas for Rect {
112 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
113 Self::new(
114 Point::from(self.min()).map_to_canvas(canvas),
115 Point::from(self.max()).map_to_canvas(canvas),
116 )
117 }
118}
119
120impl MapToCanvas for Bounds2D {
121 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
122 match self.rect() {
123 Some(rect) => Self::new(
124 rect.min().x_y().map_to_canvas(canvas).into(),
125 rect.max().x_y().map_to_canvas(canvas).into(),
126 ),
127 None => Self::default(),
128 }
129 }
130}
131
132impl MapToCanvas for Circle {
133 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
134 Self {
135 radius: self.radius.map_to_canvas(canvas),
136 offset: self.offset.map_to_canvas(canvas),
137 }
138 }
139}
140
141impl MapToCanvas for LineString {
142 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
143 Self(
144 self.0
145 .iter()
146 .map(|p| p.x_y().map_to_canvas(canvas).into())
147 .collect(),
148 )
149 }
150}
151
152impl MapToCanvas for MultiLineString {
153 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
154 self.iter()
155 .map(|line_string| line_string.map_to_canvas(canvas))
156 .collect()
157 }
158}
159
160impl MapToCanvas for Polygon {
161 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
162 Self::new(
163 self.exterior().map_to_canvas(canvas),
164 self.interiors()
165 .iter()
166 .map(|line_string| line_string.map_to_canvas(canvas))
167 .collect(),
168 )
169 }
170}
171
172impl MapToCanvas for MultiPolygon {
173 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
174 self.0
175 .iter()
176 .map(|polygon| polygon.map_to_canvas(canvas))
177 .collect()
178 }
179}
180
181impl MapToCanvas for Geometries2D {
182 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
183 Geometries2D::new(self.iter().map(|geo| geo.map_to_canvas(canvas)).collect())
184 }
185}
186
187impl MapToCanvas for Geometry2D {
188 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
189 match self {
190 Geometry2D::LineString(line_string) => {
191 Geometry2D::LineString(line_string.map_to_canvas(canvas))
192 }
193 Geometry2D::MultiLineString(multi_line_string) => {
194 Geometry2D::MultiLineString(multi_line_string.map_to_canvas(canvas))
195 }
196 Geometry2D::Polygon(polygon) => Geometry2D::Polygon(polygon.map_to_canvas(canvas)),
197 Geometry2D::MultiPolygon(multi_polygon) => {
198 Geometry2D::MultiPolygon(multi_polygon.map_to_canvas(canvas))
199 }
200 Geometry2D::Rect(rect) => Geometry2D::Rect(rect.map_to_canvas(canvas)),
201 Geometry2D::Line(edge) => Geometry2D::Line(edge.map_to_canvas(canvas)),
202 Geometry2D::Collection(collection) => {
203 Geometry2D::Collection(collection.map_to_canvas(canvas))
204 }
205 }
206 }
207}
208
209impl MapToCanvas for CenteredText {
210 fn map_to_canvas(&self, canvas: &Canvas) -> Self {
211 CenteredText {
212 text: self.text.clone(),
213 rect: self.rect.map_to_canvas(canvas),
214 font_size: self.font_size,
215 }
216 }
217}