This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1use self::Shape::*; 2use crate::{Containable, Object, Point, Region}; 3use anyhow::anyhow; 4 5#[derive(Debug, Clone, PartialEq, Eq)] 6pub enum LineSegment { 7 Straight(Point), 8 InwardCurve(Point), 9 OutwardCurve(Point), 10} 11 12#[derive(Debug, Clone)] 13pub enum Shape { 14 Polygon(Point, Vec<LineSegment>), 15 Line(Point, Point, f32), 16 CurveOutward(Point, Point, f32), 17 CurveInward(Point, Point, f32), 18 SmallCircle(Point), 19 BigDot(Point), 20 Dot(Point), 21 BigCircle(Point), 22 Text(Point, String, f32), 23 CenteredText(Point, String, f32), 24 // FittedText(Region, String), 25 Rectangle(Point, Point), 26 Image(Region, String), 27 RawSVG { 28 content: String, 29 /// String to search & replace in the SVG content when applying a color fill to the shape (see `Object`). For example, `#c0ffee`. 30 color: String, 31 }, 32 // Tiling(Region, Box<Object>), 33 Component { 34 at: Point, 35 size: (usize, usize), 36 objects: Box<Vec<Object>>, 37 }, 38} 39 40impl Shape { 41 pub fn translate(&mut self, dx: i32, dy: i32) { 42 match self { 43 Polygon(start, lines) => { 44 start.translate(dx, dy); 45 for line in lines { 46 match line { 47 LineSegment::InwardCurve(anchor) 48 | LineSegment::OutwardCurve(anchor) 49 | LineSegment::Straight(anchor) => { 50 anchor.translate(dx, dy) 51 } 52 } 53 } 54 } 55 Line(start, end, _) 56 | CurveInward(start, end, _) 57 | CurveOutward(start, end, _) 58 | Rectangle(start, end) => { 59 start.translate(dx, dy); 60 end.translate(dx, dy); 61 } 62 Text(anchor, _, _) 63 | CenteredText(anchor, ..) 64 | Dot(anchor) 65 | BigDot(anchor) => anchor.translate(dx, dy), 66 BigCircle(center) | SmallCircle(center) => center.translate(dx, dy), 67 Image(region, ..) => region.translate(dx, dy), 68 Component { at, .. } => at.translate(dx, dy), 69 RawSVG { .. } => { 70 unimplemented!() 71 } 72 } 73 } 74 75 pub fn position(&self) -> Point { 76 match self { 77 Polygon(at, ..) 78 | Line(at, ..) 79 | CurveInward(at, ..) 80 | CurveOutward(at, ..) 81 | Rectangle(at, ..) 82 | Text(at, ..) 83 | CenteredText(at, ..) 84 | Dot(at) 85 | BigDot(at) 86 | BigCircle(at) 87 | SmallCircle(at) 88 | Component { at, .. } 89 | Image(Region { start: at, .. }, ..) => *at, 90 RawSVG { .. } => { 91 unimplemented!() 92 } 93 } 94 } 95 96 pub fn translate_with(&mut self, delta: (i32, i32)) { 97 self.translate(delta.0, delta.1) 98 } 99 100 pub fn teleport(&mut self, x: i32, y: i32) { 101 let (current_x, current_y) = self.region().start.xy::<i32>(); 102 let delta_x = x - current_x; 103 let delta_y = y - current_y; 104 self.translate(delta_x, delta_y); 105 } 106 107 pub fn teleport_with(&mut self, position: (i32, i32)) { 108 self.teleport(position.0, position.1) 109 } 110 111 pub fn region(&self) -> Region { 112 match self { 113 Polygon(start, lines) => { 114 let mut region: Region = (start, start).into(); 115 for line in lines { 116 match line { 117 LineSegment::InwardCurve(anchor) 118 | LineSegment::OutwardCurve(anchor) 119 | LineSegment::Straight(anchor) => { 120 // println!( 121 // "extending region {} with {}", 122 // region, 123 // Region::from((start, anchor)) 124 // ); 125 region = *region.max(&(start, anchor).into()) 126 } 127 } 128 } 129 // println!("region for {:?} -> {}", self, region); 130 region 131 } 132 Line(s, e, _) | CurveInward(s, e, _) | CurveOutward(s, e, _) => { 133 let (x1, y1, x2, y2) = (s.x(), s.y(), e.y(), e.x()); 134 135 let region = Region::new( 136 (x1.min(x2), y1.min(y2)), 137 (x1.max(x2), y1.max(y2)), 138 ) 139 .map_err(|e| { 140 anyhow!("Could not construct region of {self:?}: {e:?}") 141 }) 142 .unwrap(); 143 144 region.enlarged( 145 if region.width() > 1 { -1 } else { 0 }, 146 if region.height() > 1 { -1 } else { 0 }, 147 ) 148 } 149 Rectangle(start, end) => { 150 Region::new(*start, *end).unwrap().enlarged(-1, -1) 151 } 152 Text(anchor, _, _) 153 | CenteredText(anchor, ..) 154 | Dot(anchor) 155 | BigDot(anchor) 156 | SmallCircle(anchor) => anchor.region(), 157 BigCircle(center) => center.region(), 158 Image(region, ..) => *region, 159 Component { at, size, .. } => Region::from_topleft(*at, *size) 160 .expect("Invalid region for component"), 161 RawSVG { .. } => { 162 unimplemented!() 163 } 164 } 165 } 166 167 pub fn fillable(&self) -> bool { 168 !matches!(self, Line(..) | CurveInward(..) | CurveOutward(..)) 169 } 170 171 pub fn hatchable(&self) -> bool { 172 self.fillable() && !matches!(self, Dot(..)) 173 } 174 175 pub fn point_is_on_line(self, point: Point) -> bool { 176 match (&self, point) { 177 (Line(s, e, _), Point::Corner(x, y)) => { 178 if !self.region().contains(&point) { 179 return false; 180 } 181 182 let (sx, sy) = s.xy::<f32>(); 183 let (ex, ey) = e.xy::<f32>(); 184 185 let m = (ey - sy) / (ex - sx); 186 let p = sy - m * sx; 187 188 (m * x as f32 + p) as usize == y 189 } 190 (Line(..), _) => panic!("Point type not supported"), 191 _ => panic!("{self:?} is not a line object"), 192 } 193 } 194 195 pub fn meets_endpoint_of_line(&self, point: Point) -> bool { 196 match self { 197 Line(s, e, _) => *s == point || *e == point, 198 _ => panic!("{self:?} is not a line object"), 199 } 200 } 201 202 /// Check if this line intersects with another line. 203 /// Panics if either shape is not a line. 204 /// 205 /// ``` 206 /// use shapemaker::{Line, Point::Center}; 207 /// let line = |x1: usize, y1: usize, x2: usize, y2: usize| 208 /// Line(Center(x1, y1), Center(x2, y2), 1.0); 209 /// assert!(line(1, 1, 4, 4).intersects_with(line(1, 4, 4, 1))); 210 /// assert!(line(7, 6, 9, 7).intersects_with(line(7, 7, 9, 4))); 211 /// assert!(line(4, 4, 6, 3).intersects_with(line(5, 2, 6, 5))); 212 /// ``` 213 pub fn intersects_with(&self, line: Shape) -> bool { 214 match (self, &line) { 215 (&Line(s1, e1, _), &Line(s2, e2, _)) => { 216 let parameters = |s: Point, e: Point| { 217 let (sx, sy) = s.xy::<isize>(); 218 let (ex, ey) = e.xy::<isize>(); 219 let a = ey - sy; 220 let b = sx - ex; 221 let c = (ex * sy) - (sx * ey); 222 (a, b, c) 223 }; 224 225 let distance_to_line = |p: Point, (s, e): (Point, Point)| { 226 let (x, y) = p.xy::<isize>(); 227 let (a, b, c) = parameters(s, e); 228 229 (a * x) + (b * y) + c 230 }; 231 232 let same_side_of_line = 233 |p1: Point, p2: Point, line: (Point, Point)| { 234 let d1 = distance_to_line(p1, line); 235 let d2 = distance_to_line(p2, line); 236 237 if (d1 == 0) || (d2 == 0) { 238 return false; 239 } 240 241 d1.signum() == d2.signum() 242 }; 243 244 if same_side_of_line(s2, e2, (s1, e1)) { 245 return false; 246 } 247 248 if same_side_of_line(s1, e1, (s2, e2)) { 249 return false; 250 } 251 252 let (a1, b1, _) = parameters(s1, e1); 253 let (a2, b2, _) = parameters(s2, e2); 254 255 (a1 * b2) != (a2 * b1) 256 } 257 _ => { 258 unimplemented!( 259 "Intersection not implemented for {self:?} and {:?}", 260 line.clone() 261 ) 262 } 263 } 264 } 265}