Libraryless. Click here for Pure Java version (10264L/59K).
1 | // Abstract base class for tiling an image (G22Tiling) |
2 | |
3 | abstract srecord noeq G22AbstractTiler<Img extends WidthAndHeight>(Img image) is Runnable, Steppable { |
4 | delegate Tile to G22Tiling. |
5 | |
6 | int w, h, runner; |
7 | gettable int size; // =w*h |
8 | |
9 | event tileStarted(GrowingTile tile); |
10 | event tileDone(GrowingTile tile); |
11 | |
12 | // iterator that provides points to be looked at |
13 | // (e.g. a WeightlessShuffledIterator) |
14 | IndexIterator pointIterator; |
15 | |
16 | // Temporary stack of points to be looked at |
17 | // (e.g. neighbors of a tile just discovered). |
18 | // May or may not be used. |
19 | new IntBuffer stack; |
20 | |
21 | G22Tiling<Img> tiling; // the tiling we are making |
22 | |
23 | int tileCounter; |
24 | GrowingTile growingTile; // tile currently being grown |
25 | |
26 | bool verbose; |
27 | |
28 | // The one method subclasses need to override. |
29 | // The exact meaning of a "color" is not defined, |
30 | // the tiler just treats different int value as different colors. |
31 | abstract int getColor(int pos); |
32 | |
33 | // calculations for positions (pixel index) |
34 | int x(int pos) { ret pos % w; } |
35 | int y(int pos) { ret pos / w; } |
36 | int pos(int x, int y) { ret y*w+x; } |
37 | Pt pt(int pos) { ret Pt(x(pos), y(pos)); } |
38 | bool validPos(int x, int y) { ret x >= 0 && y >= 0 && x < w && y < h; } |
39 | |
40 | int getColor(int x, int y) { ret getColor(pos(x, y)); } |
41 | |
42 | class GrowingTile is Steppable { |
43 | Tile tile; |
44 | int color; |
45 | |
46 | // Still growing in any of the 4 directions? |
47 | bool growingN = true; |
48 | bool growingE = true; |
49 | bool growingS = true; |
50 | bool growingW = true; |
51 | |
52 | *(int pos) { |
53 | int x = x(pos), y = y(pos); |
54 | color = getColor(x, y); |
55 | int iTile = tileCounter++; |
56 | tile = tiling.new Tile(iTile, color, rect(x, y, 1, 1)); |
57 | tiling.tiles.add(tile); |
58 | } |
59 | |
60 | // put tile into tileMatrix |
61 | void finish { |
62 | Rect r = tile.position(); |
63 | int rx = r.x, ry = r.y, rw = r.w, rh = r.h; |
64 | int[] matrix = tiling.tileMatrix; |
65 | int iTile = tile.index; |
66 | for y to rh: |
67 | for x to rw: { |
68 | int pos = (ry+y)*w+(rx+x); |
69 | if (matrix[pos] != 0) |
70 | fail("Overlapping tiles!"); |
71 | matrix[pos] = iTile+1; |
72 | } |
73 | tiling.pixelsCovered += rw*rh; |
74 | } |
75 | |
76 | public bool step() { |
77 | Rect r = nextRect(); |
78 | if (r == null) { |
79 | finish(); |
80 | false; |
81 | } |
82 | |
83 | tile.position(r); |
84 | true; |
85 | } |
86 | |
87 | // Is this pixel in the image and the tile's color |
88 | // and not in a tile? |
89 | bool isContinuation(int x, int y) { |
90 | ret validPos(x, y) && isContinuation_impl(x, y); |
91 | } |
92 | |
93 | bool isContinuation_impl(int x, int y) { |
94 | ret tiling.tileMatrix[pos(x, y)] == 0 && getColor(x, y) == color; |
95 | } |
96 | |
97 | // Is this rectangle completely in the image and the tile's color |
98 | // and not in a tile? |
99 | bool isContinuation(int x1, int y1, int w, int h) { |
100 | if (!validPos(x1, y1) || !validPos(x1+w-1, y1+h-1)) false; |
101 | for y to h: |
102 | for x to w: |
103 | if (!isContinuation_impl(x1+x, y1+y)) |
104 | false; |
105 | true; |
106 | } |
107 | |
108 | Rect nextRect() { |
109 | Rect r = tile.position(); |
110 | |
111 | // Check the 4 sides for possible growth |
112 | growingN = growingN && isContinuation(r.x, r.y-1, r.w, 1); |
113 | growingS = growingS && isContinuation(r.x, r.y2(), r.w, 1); |
114 | growingW = growingW && isContinuation(r.x-1, r.y, 1, r.h); |
115 | growingE = growingE && isContinuation(r.x2(), r.y, 1, r.h); |
116 | |
117 | // Check the corners too (including the adjacent sides) |
118 | bool cornerNW = growingN && growingW && isContinuation(r.x-1, r.y-1); |
119 | bool cornerNE = growingN && growingE && isContinuation(r.x2(), r.y-1); |
120 | bool cornerSW = growingS && growingW && isContinuation(r.x-1, r.y2()); |
121 | bool cornerSE = growingS && growingE && isContinuation(r.x2(), r.y2()); |
122 | |
123 | // Corners are the best case, so try those first |
124 | if (cornerNW) { |
125 | int x2 = r.x2(), y2 = r.y2(); // Default case: Grow to NE only |
126 | |
127 | if (cornerSW && cornerNE && cornerSE) { |
128 | // Best case - grow in all 4 directions |
129 | x2++; y2++; |
130 | } else if (cornerNE) |
131 | // At least grow to the right too |
132 | x2++; |
133 | else if (cornerSW) |
134 | // At least grow to the south too |
135 | y2++; |
136 | |
137 | ret rectFromPoints(r.x-1, r.y-1, x2, y2); |
138 | } else if (cornerNE) { |
139 | // Can't grow NW but will grow NE. So only south growth to decide |
140 | int y2 = r.y2(); |
141 | |
142 | if (cornerSE) |
143 | // Grow south too |
144 | y2++; |
145 | |
146 | ret rectFromPoints(r.x, r.y-1, r.x2()+1, y2); |
147 | } else if (cornerSW) { |
148 | // Can't grow NW or NE but will grow SW. So only east growth to decide |
149 | int x2 = r.x2(); |
150 | |
151 | if (cornerSE) |
152 | // Grow east too |
153 | x2++; |
154 | |
155 | ret rectFromPoints(r.x-1, r.y, x2, r.y2()+1); |
156 | } else if (cornerSE) { |
157 | // Only growable corner is SE, so just do it |
158 | ret rectFromPoints(r.x, r.y, r.x2()+1, r.y2()+1); |
159 | } else { |
160 | // No growable corners. Try growing west/east or north/south |
161 | |
162 | if (growingW || growingE) |
163 | ret rectFromPoints(r.x-(growingW ? 1 : 0), r.y, |
164 | r.x2()+(growingE ? 1 : 0), r.y2()); |
165 | else if (growingN || growingS) |
166 | ret rectFromPoints(r.x, r.y-(growingN ? 1 : 0), |
167 | r.x2(), r.y2()+(growingS ? 1 : 0)); |
168 | } |
169 | |
170 | // No more growth possible |
171 | null; |
172 | } |
173 | } |
174 | |
175 | run { stepAll(this); } |
176 | |
177 | public bool step() { |
178 | init(); |
179 | |
180 | // Currently growing a tile? Continue that |
181 | if (growingTile != null) { |
182 | if (growingTile.step()) true; |
183 | tileDone(growingTile); |
184 | growingTile = null; // Done growing this tile |
185 | } |
186 | |
187 | int pos = pointIterator.nextIndex(); |
188 | if (pos < 0) false; |
189 | |
190 | // Is point already covered by a tile? |
191 | if (tiling.tileMatrix[pos] != 0) true; |
192 | |
193 | // Create new tile |
194 | growingTile = new GrowingTile(pos); |
195 | tileStarted(growingTile); |
196 | |
197 | true; |
198 | } |
199 | |
200 | void init { |
201 | if (tiling != null) ret; |
202 | w = image.getWidth(); h = image.getHeight(); |
203 | size = w*h; |
204 | tiling = new G22Tiling(image); |
205 | tiling.initTileMatrix(); |
206 | |
207 | pointIterator = WeightlessShuffledIterator(size); |
208 | } |
209 | |
210 | G22Tiling<Img> get() { |
211 | if (tiling == null) run(); |
212 | ret tiling; |
213 | } |
214 | } |
Began life as a copy of #1034520
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1035226 |
Snippet name: | G22AbstractTiler - make G22Tiling / cover image by growing rectangles [seems to work] |
Eternal ID of this version: | #1035226/26 |
Text MD5: | 7f2c059c0bbd46d87c65d99a3c386a53 |
Transpilation MD5: | 8b36b920457c2377520de1b133161d27 |
Author: | stefan |
Category: | javax / imaging |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-06-23 23:57:09 |
Source code size: | 6479 bytes / 214 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 175 / 384 |
Version history: | 25 change(s) |
Referenced in: | [show references] |