Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java @ 657:af3f56758f59
merged gnv-artifacts/0.5
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:13:53 +0200 |
parents | 92b7ccbf6163 |
children | 9a828e5a2390 |
comparison
equal
deleted
inserted
replaced
590:5f5f273c8566 | 657:af3f56758f59 |
---|---|
1 package de.intevation.gnv.raster; | |
2 | |
3 import java.util.BitSet; | |
4 import java.util.List; | |
5 import java.util.ArrayList; | |
6 | |
7 import gnu.trove.TIntStack; | |
8 import gnu.trove.TIntObjectHashMap; | |
9 | |
10 import org.apache.log4j.Logger; | |
11 | |
12 /** | |
13 * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) | |
14 */ | |
15 public class Vectorizer | |
16 { | |
17 private static Logger log = Logger.getLogger(Vectorizer.class); | |
18 | |
19 public interface RingsHandler { | |
20 | |
21 void handleRings( | |
22 List<Edge> rings, | |
23 int value, | |
24 int width, | |
25 int height); | |
26 | |
27 } // interface RingsHandler | |
28 | |
29 public static final class Edge { | |
30 | |
31 public Edge prev; | |
32 public Edge next; | |
33 | |
34 public int a; | |
35 public int b; | |
36 | |
37 public Edge() { | |
38 } | |
39 | |
40 public Edge(int a, int b) { | |
41 this.a = a; | |
42 this.b = b; | |
43 } | |
44 | |
45 public Edge(Edge other) { | |
46 a = other.a; | |
47 b = other.b; | |
48 } | |
49 | |
50 public void chain(Edge other, int found) { | |
51 | |
52 if (found == a) { | |
53 other.next = this; | |
54 prev = other; | |
55 return; | |
56 } | |
57 | |
58 if (found == b) { | |
59 next = other; | |
60 other.prev = this; | |
61 return; | |
62 } | |
63 | |
64 throw new IllegalStateException("cannot chain"); | |
65 } | |
66 | |
67 public boolean isComplete() { | |
68 Edge current = this; | |
69 do { | |
70 if (current.prev == null || current.next == null) { | |
71 return false; | |
72 } | |
73 current = current.next; | |
74 } | |
75 while (current != this); | |
76 return true; | |
77 } | |
78 | |
79 public int length() { | |
80 int length = 0; | |
81 Edge current = this; | |
82 do { ++length; } | |
83 while ((current = current.next) != null && current != this); | |
84 return length; | |
85 } | |
86 | |
87 public Edge head() { | |
88 Edge current = this; | |
89 while (current.prev != null) { | |
90 current = current.prev; | |
91 } | |
92 return current; | |
93 } | |
94 | |
95 public int hashCode() { | |
96 return (a << 16) | b; | |
97 } | |
98 | |
99 public boolean equals(Object other) { | |
100 Edge e = (Edge)other; | |
101 return a == e.a && b == e.b; | |
102 } | |
103 } // class Edge | |
104 | |
105 protected static Edge simplify(Edge edge, int width) { | |
106 | |
107 Edge e1 = edge, start = edge; | |
108 | |
109 int length = edge.length(); | |
110 | |
111 if (length < 2) { | |
112 return e1; | |
113 } | |
114 | |
115 Edge e2 = edge.next; | |
116 | |
117 int count = 0; | |
118 | |
119 do { | |
120 int e1x = e1.a % width; | |
121 int e1y = e1.a / width; | |
122 int e2x = e1.b % width; | |
123 int e2y = e1.b / width; | |
124 int e3x = e2.b % width; | |
125 int e3y = e2.b / width; | |
126 | |
127 if ((e1x == e2x && e2x == e3x && e1x == e3x) | |
128 || (e1y == e2y && e2y == e3y && e1y == e3y)) { | |
129 e1.b = e2.b; | |
130 Edge removed = e1.next; | |
131 e1.next = e2.next; | |
132 if (e1.next != null) { | |
133 e1.next.prev = e1; | |
134 } | |
135 e2 = e1.next; | |
136 count = 0; | |
137 --length; | |
138 if (removed == start) { | |
139 start = e1; | |
140 } | |
141 } | |
142 else { | |
143 e1 = e1.next; | |
144 e2 = e2.next; | |
145 ++count; | |
146 } | |
147 } | |
148 while (length > 1 && e2 != null && count < length + 2); | |
149 | |
150 return start; | |
151 } | |
152 | |
153 protected int [] raster; | |
154 protected int width; | |
155 protected TIntObjectHashMap openEdges; | |
156 protected List<Edge> rings; | |
157 protected boolean simplify; | |
158 | |
159 public Vectorizer() { | |
160 this(true); | |
161 } | |
162 | |
163 public Vectorizer(boolean simplify) { | |
164 openEdges = new TIntObjectHashMap(); | |
165 rings = new ArrayList<Edge>(); | |
166 this.simplify = simplify; | |
167 } | |
168 | |
169 public Vectorizer(int [] raster, int width) { | |
170 this(true, raster, width); | |
171 } | |
172 | |
173 public Vectorizer(boolean simplify, int [] raster, int width) { | |
174 this(simplify); | |
175 this.raster = raster; | |
176 this.width = width; | |
177 } | |
178 | |
179 public static final int tl(int i, int w) { | |
180 int x = i % w; | |
181 int y = i / w; | |
182 return x + (w + 1)*y; | |
183 } | |
184 | |
185 public static final int tr(int i, int w) { | |
186 return tl(i, w) + 1; | |
187 } | |
188 | |
189 public static final int bl(int i, int w) { | |
190 return tl(i, w) + w + 1; | |
191 } | |
192 | |
193 public static final int br(int i, int w) { | |
194 return bl(i, w) + 1; | |
195 } | |
196 | |
197 protected void resetRegion() { | |
198 openEdges.clear(); | |
199 rings.clear(); | |
200 } | |
201 | |
202 protected void emit(Edge edge) { | |
203 | |
204 Edge otherA = (Edge)openEdges.remove(edge.a); | |
205 if (otherA != null) { | |
206 otherA.chain(edge, edge.a); | |
207 } | |
208 | |
209 Edge otherB = (Edge)openEdges.remove(edge.b); | |
210 if (otherB != null) { | |
211 otherB.chain(edge, edge.b); | |
212 } | |
213 | |
214 if (edge.isComplete()) { | |
215 rings.add(simplify ? simplify(edge, width + 1) : edge); | |
216 } | |
217 else { | |
218 if (otherA == null) { | |
219 openEdges.put(edge.a, edge); | |
220 } | |
221 if (otherB == null) { | |
222 openEdges.put(edge.b, edge); | |
223 } | |
224 } | |
225 } | |
226 | |
227 public int process(RingsHandler handler) { | |
228 | |
229 BitSet visited = new BitSet(raster.length); | |
230 | |
231 TIntStack stack = new TIntStack(); | |
232 | |
233 int regions = 0; | |
234 | |
235 int height = raster.length / width; | |
236 | |
237 for (int i = 0; i < raster.length; ++i) { | |
238 if (visited.get(i)) { | |
239 continue; | |
240 } | |
241 | |
242 ++regions; | |
243 | |
244 int currentValue = raster[i]; | |
245 visited.set(i); | |
246 | |
247 int current = i; | |
248 visited.set(current); | |
249 | |
250 for (;;) { | |
251 int tl = tl(current, width); | |
252 int tr = tr(current, width); | |
253 int bl = bl(current, width); | |
254 int br = br(current, width); | |
255 | |
256 int t = current - width; | |
257 | |
258 if (t < 0) { | |
259 emit(new Edge(tr, tl)); | |
260 } | |
261 else { | |
262 if (raster[t] != currentValue) { | |
263 emit(new Edge(tr, tl)); | |
264 } | |
265 else { | |
266 if (!visited.get(t)) { | |
267 visited.set(t); | |
268 stack.push(t); | |
269 } | |
270 } | |
271 } | |
272 | |
273 int b = current + width; | |
274 | |
275 if (b >= raster.length) { | |
276 emit(new Edge(bl, br)); | |
277 } | |
278 else { | |
279 if (raster[b] != currentValue) { | |
280 emit(new Edge(bl, br)); | |
281 } | |
282 else { | |
283 if (!visited.get(b)) { | |
284 visited.set(b); | |
285 stack.push(b); | |
286 } | |
287 } | |
288 } | |
289 | |
290 int x = current % width; | |
291 | |
292 if (x == 0) { | |
293 emit(new Edge(tl, bl)); | |
294 } | |
295 else { | |
296 int l = current - 1; | |
297 if (raster[l] != currentValue) { | |
298 emit(new Edge(tl, bl)); | |
299 } | |
300 else { | |
301 if (!visited.get(l)) { | |
302 visited.set(l); | |
303 stack.push(l); | |
304 } | |
305 } | |
306 } | |
307 | |
308 if (x == width - 1) { | |
309 emit(new Edge(br, tr)); | |
310 } | |
311 else { | |
312 int r = current + 1; | |
313 if (raster[r] != currentValue) { | |
314 emit(new Edge(br, tr)); | |
315 } | |
316 else { | |
317 if (!visited.get(r)) { | |
318 visited.set(r); | |
319 stack.push(r); | |
320 } | |
321 } | |
322 } | |
323 | |
324 if (stack.size() == 0) { | |
325 break; | |
326 } | |
327 | |
328 current = stack.pop(); | |
329 } | |
330 | |
331 handler.handleRings( | |
332 rings, | |
333 currentValue, | |
334 width + 1, | |
335 height + 1); | |
336 | |
337 resetRegion(); | |
338 } | |
339 | |
340 return regions; | |
341 } | |
342 } | |
343 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: |