Transpiled version (11072L) is out of date.
1 | // A "mesh" is a set of points ("anchors") connected through curves. |
2 | |
3 | // An anchor is either |
4 | // -the end of a curve |
5 | // -an intersection of curves |
6 | // -or a randomly chosen point along a curve (type 3) |
7 | |
8 | // A type 3 anchor can be necessary to define an "o" shape |
9 | // (because such a shape doesn't have an anchor point per se, |
10 | // so we just choose one point along the ring). |
11 | |
12 | // Sometimes however, a type 3 anchor is an artifact of the mesh |
13 | // finding process and can be eliminated, reducing the anchor and |
14 | // curve count by 1. |
15 | |
16 | // G22Mesh has identity-based equality although it would be possible |
17 | // to define a value-based equality function. |
18 | |
19 | persistable sclass G22Mesh > Meta is MakesBufferedImage { |
20 | new LinkedHashMap<Pt, Anchor> anchorMap; // sorted by index |
21 | gettable new LinkedHashSet<Curve> curves; |
22 | |
23 | // cached signature |
24 | S cachedSignature; |
25 | |
26 | persistable sclass Curve { |
27 | gettable Anchor start; |
28 | gettable Anchor end; |
29 | gettable OnePathWithOrigin path; // origin = start.pt |
30 | |
31 | *(Anchor start, Anchor end, L<Pt> points) { |
32 | this(start, end, new OnePathWithOrigin(points, false)); |
33 | } |
34 | |
35 | *(Anchor *start, Anchor *end, OnePathWithOrigin *path) { |
36 | start.outgoingCurves.add(this); |
37 | end.incomingCurves.add(this); |
38 | } |
39 | |
40 | toString { |
41 | ret "Curve of length " + n2(path.nSteps()) |
42 | + " connecting " + start?.shortToString() + " and " + end?.shortToString(); |
43 | } |
44 | |
45 | // This counts one of the anchors, but not the other. |
46 | // So if the anchors are right next to each other, |
47 | // the curve's length is 1. |
48 | int length() { ret path.nSteps(); } |
49 | |
50 | L<Anchor> anchors() { ret ll(start, end); } |
51 | |
52 | bool connectedTo(Anchor a) { |
53 | ret start == a || end == a; |
54 | } |
55 | |
56 | Anchor anchor(bool endAnchor) { ret endAnchor ? end : start; } |
57 | |
58 | Anchor anchorOpposite(Anchor a) { |
59 | ret a == start ? end : a == end ? start : null; |
60 | } |
61 | } // end of Curve |
62 | |
63 | persistable sclass Anchor { |
64 | int index; // every anchor gets a number starting from one |
65 | gettable Pt pt; |
66 | new L<Curve> outgoingCurves; |
67 | new L<Curve> incomingCurves; |
68 | |
69 | *(int *index, Pt *pt) {} |
70 | |
71 | int arity() { ret l(outgoingCurves) + l(incomingCurves); } |
72 | |
73 | S shortToString() { |
74 | ret "Anchor " + index + " at " + pt; |
75 | } |
76 | |
77 | toString { |
78 | L<Int> connections = sorted(map(connectedToAnchors(), a -> a.index)); |
79 | ret shortToString() + ", arity " + arity() |
80 | + stringIf(isRingAnchor(), " [arbitrarily chosen ring anchor]") |
81 | + (empty(connections) ? "" : ", connected to " + joinWithComma(connections)); |
82 | } |
83 | |
84 | Set<Curve> curves() { |
85 | ret joinSets(outgoingCurves, incomingCurves); |
86 | } |
87 | |
88 | Set<Anchor> connectedToAnchors() { |
89 | ret joinSets( |
90 | map(outgoingCurves, c -> c.end), |
91 | map(incomingCurves, c -> c.start)); |
92 | } |
93 | |
94 | // The ring case where we just randomly select an anchor |
95 | bool isRingAnchor() { |
96 | ret l(outgoingCurves) == 1 && l(incomingCurves) == 1 |
97 | && first(outgoingCurves) == first(incomingCurves); |
98 | } |
99 | } // end of Anchor |
100 | |
101 | Cl<Pt> anchorPts() { ret keys(anchorMap); } |
102 | Cl<Anchor> anchors() { ret values(anchorMap); } |
103 | int nAnchors() { ret l(anchorMap); } |
104 | L<Anchor> anchorList() { ret valuesList(anchorMap); } |
105 | Anchor getAnchor(Pt p) { ret anchorMap.get(p); } |
106 | Anchor getAnchor(int idx) { ret anchorList().get(idx); } |
107 | |
108 | // Internal structure sanity check |
109 | void checkArities { |
110 | new MultiMap<Anchor, Curve> outgoing; |
111 | new MultiMap<Anchor, Curve> incoming; |
112 | |
113 | for (Curve curve : curves) { |
114 | outgoing.add(curve.start, curve); |
115 | if (!anchorMap.containsKey(curve.start.pt)) |
116 | fail("Start of curve not found: " + curve); |
117 | incoming.add(curve.end, curve); |
118 | if (!anchorMap.containsKey(curve.end.pt)) |
119 | fail("End of curve not found: " + curve); |
120 | } |
121 | |
122 | for (Anchor anchor : anchors()) { |
123 | assertSetEquals(anchor + " outgoing", outgoing.get(anchor), anchor.outgoingCurves); |
124 | assertSetEquals(anchor + " incoming", incoming.get(anchor), anchor.incomingCurves); |
125 | } |
126 | } |
127 | |
128 | Anchor newAnchor aka addAnchor(Pt p) { |
129 | Anchor anchor = new(l(anchorMap)+1, p); |
130 | anchorMap.put(p, anchor); |
131 | _invalidateSignature(); |
132 | ret anchor; |
133 | } |
134 | |
135 | void removeAnchor(Anchor anchor) { |
136 | anchorMap.remove(anchor.pt); |
137 | } |
138 | |
139 | Curve addCurve(Curve curve) { |
140 | curves.add(curve); |
141 | _invalidateSignature(); |
142 | ret curve; |
143 | } |
144 | |
145 | void removeCurve(Curve curve) { |
146 | curves.remove(curve); |
147 | curve.start.outgoingCurves.remove(curve); |
148 | curve.end.incomingCurves.remove(curve); |
149 | _invalidateSignature(); |
150 | } |
151 | |
152 | // all anchor arities in descending order |
153 | // (a sort of fingerprint of the mesh) |
154 | int[] sortedArities() { |
155 | int[] array = mapToIntArray(anchors(), a -> a.arity()); |
156 | ret sortIntArrayInPlaceDesc(array); |
157 | } |
158 | |
159 | S sortedAritiesToString aka aritySignature aka signature() { |
160 | try object cachedSignature; |
161 | int[] arities = sortedArities(); |
162 | bool compact = all(arities, arity -> arity < 10); |
163 | ret cachedSignature = compact |
164 | ? join(asList(arities)) |
165 | : roundBracket(joinWithComma(asList(arities))); |
166 | } |
167 | |
168 | toString { |
169 | ret "Mesh type " + sortedAritiesToString(); |
170 | } |
171 | |
172 | // re-number anchors starting from 1 after anchors were removed |
173 | void renumberAnchors { |
174 | int i = 0; |
175 | for (anchor : anchors()) |
176 | anchor.index = ++i; |
177 | } |
178 | |
179 | public Rect bounds() { |
180 | new BoundsFinder bf; |
181 | for (p : keys(anchorMap)) bf.add(p); |
182 | for (curve : curves) |
183 | for (p : curve.path.pointIterator()) |
184 | bf.add(p); |
185 | ret bf!; |
186 | } |
187 | |
188 | public int getWidth() { ret bounds().w; } |
189 | public int getHeight() { ret bounds().h; } |
190 | |
191 | public BufferedImage getBufferedImage() { |
192 | var r = bounds(); |
193 | ret new G22VisualizeMeshes(widthAndHeight(r.x2()+2, r.y2()+2), ll(this))!; |
194 | } |
195 | |
196 | void _invalidateSignature { |
197 | cachedSignature = null; |
198 | } |
199 | |
200 | bool containsAnchor(Anchor a) { |
201 | ret a != null && anchorMap.get(a.pt) == a; |
202 | } |
203 | |
204 | bool containsCurve(Curve c) { |
205 | ret curves.contains(c); |
206 | } |
207 | |
208 | L<Curve> curveList() { ret asList(curves); } |
209 | |
210 | // SOME OPERATIONS |
211 | |
212 | void moveAnchor(Anchor a, Pt p) { |
213 | if (eq(a.pt(), p)) ret; |
214 | assertNotNull(p); |
215 | anchorMap.remove(a.pt); |
216 | a.pt = p; |
217 | anchorMap.put(p, a); |
218 | } |
219 | |
220 | // coalesce a1 into a2, keeping a2's position intact |
221 | void mergeAnchorInto(Anchor a1, Anchor a2) { |
222 | mergeAnchors(a1, a2, a2.pt); |
223 | } |
224 | |
225 | // merges a1 and a2 (and their connections) into a new anchor |
226 | // at newPosition |
227 | void mergeAnchors(Anchor a1, Anchor a2, Pt newPosition) { |
228 | if (scaffoldingEnabled()) |
229 | printVars("mergeAnchors", mesh := this, +a1, +a2, +newPosition); |
230 | assertNotSame(a1, a2); |
231 | assertNotNull(a1); |
232 | assertNotNull(a2); |
233 | |
234 | for (Curve curve : a1.outgoingCurves) { |
235 | // first step is now going from a2 to a1 |
236 | curve.path.insertStep(0, ptMinus(a1.pt, a2.pt)); |
237 | curve.path.origin(a2.pt); |
238 | curve.start = a2; |
239 | a2.outgoingCurves.add(curve); |
240 | } |
241 | |
242 | for (Curve curve : a1.incomingCurves) { |
243 | // last step is now going from a1 to a2 |
244 | curve.path.addStep(ptMinus(a2.pt, a1.pt)); |
245 | curve.end = a2; |
246 | a2.incomingCurves.add(curve); |
247 | } |
248 | |
249 | moveAnchor(a2, newPosition); |
250 | removeAnchor(a1); |
251 | } |
252 | } |
Began life as a copy of #1035010
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1035027 |
Snippet name: | G22Mesh |
Eternal ID of this version: | #1035027/48 |
Text MD5: | cbdaeebb172eea2dc1c43b8616a581c6 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-05-22 15:19:59 |
Source code size: | 7519 bytes / 252 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 252 / 538 |
Version history: | 47 change(s) |
Referenced in: | [show references] |