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:

http://dive4elements.wald.intevation.org