This repository has no description
1use crate::{Fill, Filter, Point, Region, Transformation};
2#[cfg(feature = "web")]
3use wasm_bindgen::prelude::*;
4
5use super::{fill::FillOperations, Color};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum LineSegment {
9 Straight(Point),
10 InwardCurve(Point),
11 OutwardCurve(Point),
12}
13
14#[derive(Debug, Clone)]
15pub enum Object {
16 Polygon(Point, Vec<LineSegment>),
17 Line(Point, Point, f32),
18 CurveOutward(Point, Point, f32),
19 CurveInward(Point, Point, f32),
20 SmallCircle(Point),
21 Dot(Point),
22 BigCircle(Point),
23 Text(Point, String, f32),
24 CenteredText(Point, String, f32),
25 // FittedText(Region, String),
26 Rectangle(Point, Point),
27 Image(Region, String),
28 RawSVG(String),
29 // Tiling(Region, Box<Object>),
30}
31
32impl Object {
33 pub fn filled(self, fill: Fill) -> ColoredObject {
34 ColoredObject::from((self, Some(fill)))
35 }
36
37 pub fn colored(self, color: Color) -> ColoredObject {
38 ColoredObject::from((self, None)).colored(color)
39 }
40
41 pub fn filtered(self, filter: Filter) -> ColoredObject {
42 ColoredObject::from((self, None)).filtered(filter)
43 }
44
45 pub fn transform(self, transformation: Transformation) -> ColoredObject {
46 ColoredObject::from((self, None)).transformed(transformation)
47 }
48}
49
50#[derive(Debug, Clone)]
51pub struct ColoredObject {
52 pub object: Object,
53 pub fill: Option<Fill>,
54 pub filters: Vec<Filter>,
55 pub transformations: Vec<Transformation>,
56}
57
58impl ColoredObject {
59 pub fn filtered(mut self, filter: Filter) -> Self {
60 self.filters.push(filter);
61 self
62 }
63
64 pub fn transformed(mut self, transformation: Transformation) -> Self {
65 self.transformations.push(transformation);
66 self
67 }
68
69 pub fn filled(mut self, fill: Fill) -> Self {
70 self.fill = Some(fill);
71 self
72 }
73
74 pub fn colored(mut self, color: Color) -> Self {
75 self.fill = Some(Fill::Solid(color));
76 self
77 }
78
79 pub fn opacified(mut self, opacity: f32) -> Self {
80 if let Some(fill) = &mut self.fill {
81 *fill = fill.opacify(opacity);
82 }
83 self
84 }
85
86 pub fn clear_filters(&mut self) {
87 self.filters.clear();
88 }
89
90 pub fn refill(&mut self, fill: Fill) {
91 self.fill = Some(fill);
92 }
93
94 pub fn recolor(&mut self, color: Color) {
95 self.fill = Some(Fill::Solid(color))
96 }
97
98 pub fn filter(&mut self, filter: Filter) {
99 self.filters.push(filter)
100 }
101
102 pub fn region(&self) -> Region {
103 self.object.region()
104 }
105}
106
107impl std::fmt::Display for ColoredObject {
108 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
109 let ColoredObject {
110 object,
111 fill,
112 filters,
113 transformations,
114 } = self;
115
116 if fill.is_some() {
117 write!(f, "{:?} {:?}", fill.unwrap(), object)?;
118 } else {
119 write!(f, "transparent {:?}", object)?;
120 }
121
122 if !filters.is_empty() {
123 write!(f, " with filters {:?}", filters)?;
124 }
125
126 if !transformations.is_empty() {
127 write!(f, " with transformations {:?}", transformations)?;
128 }
129
130 Ok(())
131 }
132}
133
134impl From<Object> for ColoredObject {
135 fn from(value: Object) -> Self {
136 ColoredObject {
137 object: value,
138 fill: None,
139 filters: vec![],
140 transformations: vec![],
141 }
142 }
143}
144
145impl From<(Object, Option<Fill>)> for ColoredObject {
146 fn from((object, fill): (Object, Option<Fill>)) -> Self {
147 ColoredObject {
148 object,
149 fill,
150 filters: vec![],
151 transformations: vec![],
152 }
153 }
154}
155
156#[cfg_attr(feature = "web", wasm_bindgen)]
157#[derive(Debug, Clone, Copy)]
158pub struct ObjectSizes {
159 pub empty_shape_stroke_width: f32,
160 pub small_circle_radius: f32,
161 pub dot_radius: f32,
162 pub default_line_width: f32,
163}
164
165impl Default for ObjectSizes {
166 fn default() -> Self {
167 Self {
168 empty_shape_stroke_width: 0.5,
169 small_circle_radius: 5.0,
170 dot_radius: 2.0,
171 default_line_width: 2.0,
172 }
173 }
174}
175
176impl Object {
177 pub fn translate(&mut self, dx: i32, dy: i32) {
178 match self {
179 Object::Polygon(start, lines) => {
180 start.translate(dx, dy);
181 for line in lines {
182 match line {
183 LineSegment::InwardCurve(anchor)
184 | LineSegment::OutwardCurve(anchor)
185 | LineSegment::Straight(anchor) => {
186 anchor.translate(dx, dy)
187 }
188 }
189 }
190 }
191 Object::Line(start, end, _)
192 | Object::CurveInward(start, end, _)
193 | Object::CurveOutward(start, end, _)
194 | Object::Rectangle(start, end) => {
195 start.translate(dx, dy);
196 end.translate(dx, dy);
197 }
198 Object::Text(anchor, _, _)
199 | Object::CenteredText(anchor, ..)
200 | Object::Dot(anchor)
201 | Object::SmallCircle(anchor) => anchor.translate(dx, dy),
202 Object::BigCircle(center) => center.translate(dx, dy),
203 Object::Image(region, ..) => region.translate(dx, dy),
204 Object::RawSVG(_) => {
205 unimplemented!()
206 }
207 }
208 }
209
210 pub fn translate_with(&mut self, delta: (i32, i32)) {
211 self.translate(delta.0, delta.1)
212 }
213
214 pub fn teleport(&mut self, x: i32, y: i32) {
215 let Point(current_x, current_y) = self.region().start;
216 let delta_x = x - current_x as i32;
217 let delta_y = y - current_y as i32;
218 self.translate(delta_x, delta_y);
219 }
220
221 pub fn teleport_with(&mut self, position: (i32, i32)) {
222 self.teleport(position.0, position.1)
223 }
224
225 pub fn region(&self) -> Region {
226 match self {
227 Object::Polygon(start, lines) => {
228 let mut region: Region = (start, start).into();
229 for line in lines {
230 match line {
231 LineSegment::InwardCurve(anchor)
232 | LineSegment::OutwardCurve(anchor)
233 | LineSegment::Straight(anchor) => {
234 // println!(
235 // "extending region {} with {}",
236 // region,
237 // Region::from((start, anchor))
238 // );
239 region = *region.max(&(start, anchor).into())
240 }
241 }
242 }
243 // println!("region for {:?} -> {}", self, region);
244 region
245 }
246 Object::Line(start, end, _)
247 | Object::CurveInward(start, end, _)
248 | Object::CurveOutward(start, end, _)
249 | Object::Rectangle(start, end) => (start, end).into(),
250 Object::Text(anchor, _, _)
251 | Object::CenteredText(anchor, ..)
252 | Object::Dot(anchor)
253 | Object::SmallCircle(anchor) => anchor.region(),
254 Object::BigCircle(center) => center.region(),
255 Object::Image(region, ..) => *region,
256 Object::RawSVG(_) => {
257 unimplemented!()
258 }
259 }
260 }
261
262 pub fn fillable(&self) -> bool {
263 !matches!(
264 self,
265 Object::Line(..) | Object::CurveInward(..) | Object::CurveOutward(..)
266 )
267 }
268
269 pub fn hatchable(&self) -> bool {
270 self.fillable() && !matches!(self, Object::Dot(..))
271 }
272}