silly little doodles

add mirroring

nnuuvv 58a0751b 59ea1c13

Changed files
+114 -34
src
+114 -34
src/doodler.gleam
··· 18 18 } 19 19 20 20 fn init(_flags) { 21 - Model(starting_points(), [], None) 21 + Model(starting_points(), [], None, Both) 22 22 } 23 23 24 24 type Model { 25 - Model(points: Set(Point), edges: List(Edge), selected: Option(Point)) 25 + Model( 26 + points: Set(Point), 27 + edges: List(Edge), 28 + selected: Option(Point), 29 + mirroring: Mirroring, 30 + ) 26 31 } 27 32 28 33 type Point { ··· 33 38 Edge(a: Point, b: Point) 34 39 } 35 40 41 + type Mirroring { 42 + Off 43 + Vertical 44 + Horizontal 45 + Both 46 + } 47 + 36 48 type Msg { 37 49 UserClickedPoint(Point) 38 50 // TODO: implement mirror toggle 39 51 // For horizontal / vertical mirroring 52 + UserClickedToggleMirror 40 53 // TODO: implement 'shape verification mode' toggle 41 54 } 42 55 43 56 fn starting_points() { 44 - set.from_list([ 45 - Point(0, 0), 46 - Point(0, 1), 47 - Point(0, -1), 48 - Point(-1, 0), 49 - Point(-1, -1), 50 - Point(1, 0), 51 - Point(1, -1), 52 - ]) 57 + set.from_list(add_point_neighbors(set.new(), Point(0, 0)) |> set.to_list()) 53 58 } 54 59 55 60 fn update(model: Model, msg: Msg) { ··· 58 63 UserClickedPoint(clicked_point) -> { 59 64 case model { 60 65 // no point selected, select the clicked one 61 - Model(points: _, edges:, selected: None) -> { 66 + Model(points: _, edges:, selected: None, mirroring:) -> { 62 67 let points = 63 - update_points(edges) 68 + edges 69 + |> update_points 64 70 |> add_point_neighbors(clicked_point) 65 71 66 - Model(points:, edges:, selected: Some(clicked_point)) 72 + Model(points:, edges:, selected: Some(clicked_point), mirroring:) 67 73 } 68 74 // same point clicked again, deselect it 69 - Model(points: _, edges:, selected: Some(first_point)) 75 + Model(points: _, edges:, selected: Some(first_point), mirroring:) 70 76 if clicked_point == first_point 71 77 -> { 72 78 let points = update_points(edges) 73 - Model(points, edges, selected: None) 79 + Model(points, edges, selected: None, mirroring:) 74 80 } 75 81 76 82 // different point selected, add / remove that edge 77 - Model(_, _, Some(first_point)) -> { 78 - // doesnt work 79 - let thing = 83 + Model(_, _, Some(first_point), mirroring:) -> { 84 + let filtered_edges = 80 85 model.edges 81 - |> list.partition(fn(edge) { 82 - { 83 - points_equal(edge.a, first_point) 84 - && points_equal(edge.b, clicked_point) 85 - } 86 - || { 87 - points_equal(edge.a, clicked_point) 88 - && points_equal(edge.b, first_point) 89 - } 90 - }) 86 + |> list.partition(filter_edge( 87 + _, 88 + first_point, 89 + clicked_point, 90 + mirroring, 91 + )) 91 92 92 - let edges = case thing { 93 + let edges = case filtered_edges { 93 94 // the new edge wasnt found in the existing ones, add it 94 - #([], edges) -> [Edge(first_point, clicked_point), ..edges] 95 + #([], edges) -> 96 + add_new_edge(first_point, clicked_point, edges, mirroring) 95 97 // the new edge was found in the existing ones, remove it 96 98 #([_, ..], edges) -> edges 97 99 } 98 100 let points = update_points(edges) 99 101 // let points = model.points 100 102 101 - Model(points:, edges:, selected: None) 103 + Model(points:, edges:, selected: None, mirroring:) 102 104 } 103 105 } 104 106 } 107 + // cycle mirroring 108 + UserClickedToggleMirror -> 109 + case model { 110 + Model(_, _, _, Off) -> Model(..model, mirroring: Vertical) 111 + Model(_, _, _, Vertical) -> Model(..model, mirroring: Horizontal) 112 + Model(_, _, _, Horizontal) -> Model(..model, mirroring: Both) 113 + Model(_, _, _, Both) -> Model(..model, mirroring: Off) 114 + } 115 + } 116 + } 117 + 118 + fn add_new_edge( 119 + point_a: Point, 120 + point_b: Point, 121 + edges: List(Edge), 122 + mirroring: Mirroring, 123 + ) -> List(Edge) { 124 + case mirroring { 125 + Off -> [Edge(point_a, point_b)] 126 + Vertical -> [ 127 + Edge(point_a, point_b), 128 + Edge(point_a, point_b) |> invert_y, 129 + ] 130 + Horizontal -> [ 131 + Edge(point_a, point_b), 132 + Edge(point_a, point_b) |> invert_x, 133 + ] 134 + Both -> [ 135 + Edge(point_a, point_b), 136 + Edge(point_a, point_b) |> invert_x, 137 + Edge(point_a, point_b) |> invert_y, 138 + Edge(point_a, point_b) |> invert_x |> invert_y(), 139 + ] 140 + } 141 + |> list.append(edges) 142 + } 143 + 144 + fn invert_x(edge: Edge) -> Edge { 145 + let Edge(a, b) = edge 146 + 147 + Edge(Point(-a.x, a.y), Point(-b.x, b.y)) 148 + } 149 + 150 + fn invert_y(edge: Edge) -> Edge { 151 + let Edge(a, b) = edge 152 + 153 + Edge(Point(a.x, -a.y), Point(b.x, -b.y)) 154 + } 155 + 156 + fn filter_edge(edge: Edge, point_a, point_b, mirroring) { 157 + { 158 + points_equal_respect_mirror(edge.a, point_a, mirroring) 159 + && points_equal_respect_mirror(edge.b, point_b, mirroring) 160 + } 161 + || { 162 + points_equal_respect_mirror(edge.a, point_b, mirroring) 163 + && points_equal_respect_mirror(edge.b, point_a, mirroring) 105 164 } 106 165 } 107 166 ··· 147 206 first_point.x == second_point.x && first_point.y == second_point.y 148 207 } 149 208 209 + /// compare two points for equality; while respecting Mirroring 210 + /// 211 + fn points_equal_respect_mirror( 212 + first_point: Point, 213 + second_point: Point, 214 + mirroring: Mirroring, 215 + ) -> Bool { 216 + case mirroring { 217 + Off -> first_point.x == second_point.x && first_point.y == second_point.y 218 + Vertical -> 219 + first_point.x == second_point.x 220 + && int.absolute_value(first_point.y) == int.absolute_value(second_point.y) 221 + Horizontal -> 222 + int.absolute_value(first_point.x) == int.absolute_value(second_point.x) 223 + && first_point.y == second_point.y 224 + Both -> 225 + int.absolute_value(first_point.x) == int.absolute_value(second_point.x) 226 + && int.absolute_value(first_point.y) == int.absolute_value(second_point.y) 227 + } 228 + } 229 + 150 230 /// get #(min_x, min_y, max_x, max_y) from a list of points 151 231 /// 152 232 fn min_max(points) { ··· 189 269 div([], [ 190 270 svg.svg( 191 271 [ 192 - attribute.attribute("width", "100vw"), 193 - attribute.attribute("height", "100vh"), 272 + attribute.attribute("width", "100%"), 273 + attribute.attribute("height", "100%"), 194 274 attribute.attribute("viewBox", viewbox), 195 275 ], 196 276 list.append(