blob: 2fc5eea0a6346a33f0b5fb480ba9d3e88cbb39a5 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.geom;
27
28import java.util.Vector;
29import java.util.Enumeration;
30import java.util.Comparator;
31import java.util.Arrays;
32
33public abstract class AreaOp {
34 public static abstract class CAGOp extends AreaOp {
35 boolean inLeft;
36 boolean inRight;
37 boolean inResult;
38
39 public void newRow() {
40 inLeft = false;
41 inRight = false;
42 inResult = false;
43 }
44
45 public int classify(Edge e) {
46 if (e.getCurveTag() == CTAG_LEFT) {
47 inLeft = !inLeft;
48 } else {
49 inRight = !inRight;
50 }
51 boolean newClass = newClassification(inLeft, inRight);
52 if (inResult == newClass) {
53 return ETAG_IGNORE;
54 }
55 inResult = newClass;
56 return (newClass ? ETAG_ENTER : ETAG_EXIT);
57 }
58
59 public int getState() {
60 return (inResult ? RSTAG_INSIDE : RSTAG_OUTSIDE);
61 }
62
63 public abstract boolean newClassification(boolean inLeft,
64 boolean inRight);
65 }
66
67 public static class AddOp extends CAGOp {
68 public boolean newClassification(boolean inLeft, boolean inRight) {
69 return (inLeft || inRight);
70 }
71 }
72
73 public static class SubOp extends CAGOp {
74 public boolean newClassification(boolean inLeft, boolean inRight) {
75 return (inLeft && !inRight);
76 }
77 }
78
79 public static class IntOp extends CAGOp {
80 public boolean newClassification(boolean inLeft, boolean inRight) {
81 return (inLeft && inRight);
82 }
83 }
84
85 public static class XorOp extends CAGOp {
86 public boolean newClassification(boolean inLeft, boolean inRight) {
87 return (inLeft != inRight);
88 }
89 }
90
91 public static class NZWindOp extends AreaOp {
92 private int count;
93
94 public void newRow() {
95 count = 0;
96 }
97
98 public int classify(Edge e) {
99 // Note: the right curves should be an empty set with this op...
100 // assert(e.getCurveTag() == CTAG_LEFT);
101 int newCount = count;
102 int type = (newCount == 0 ? ETAG_ENTER : ETAG_IGNORE);
103 newCount += e.getCurve().getDirection();
104 count = newCount;
105 return (newCount == 0 ? ETAG_EXIT : type);
106 }
107
108 public int getState() {
109 return ((count == 0) ? RSTAG_OUTSIDE : RSTAG_INSIDE);
110 }
111 }
112
113 public static class EOWindOp extends AreaOp {
114 private boolean inside;
115
116 public void newRow() {
117 inside = false;
118 }
119
120 public int classify(Edge e) {
121 // Note: the right curves should be an empty set with this op...
122 // assert(e.getCurveTag() == CTAG_LEFT);
123 boolean newInside = !inside;
124 inside = newInside;
125 return (newInside ? ETAG_ENTER : ETAG_EXIT);
126 }
127
128 public int getState() {
129 return (inside ? RSTAG_INSIDE : RSTAG_OUTSIDE);
130 }
131 }
132
133 private AreaOp() {
134 }
135
136 /* Constants to tag the left and right curves in the edge list */
137 public static final int CTAG_LEFT = 0;
138 public static final int CTAG_RIGHT = 1;
139
140 /* Constants to classify edges */
141 public static final int ETAG_IGNORE = 0;
142 public static final int ETAG_ENTER = 1;
143 public static final int ETAG_EXIT = -1;
144
145 /* Constants used to classify result state */
146 public static final int RSTAG_INSIDE = 1;
147 public static final int RSTAG_OUTSIDE = -1;
148
149 public abstract void newRow();
150
151 public abstract int classify(Edge e);
152
153 public abstract int getState();
154
155 public Vector calculate(Vector left, Vector right) {
156 Vector edges = new Vector();
157 addEdges(edges, left, AreaOp.CTAG_LEFT);
158 addEdges(edges, right, AreaOp.CTAG_RIGHT);
159 edges = pruneEdges(edges);
160 if (false) {
161 System.out.println("result: ");
162 int numcurves = edges.size();
163 Curve[] curvelist = (Curve[]) edges.toArray(new Curve[numcurves]);
164 for (int i = 0; i < numcurves; i++) {
165 System.out.println("curvelist["+i+"] = "+curvelist[i]);
166 }
167 }
168 return edges;
169 }
170
171 private static void addEdges(Vector edges, Vector curves, int curvetag) {
172 Enumeration enum_ = curves.elements();
173 while (enum_.hasMoreElements()) {
174 Curve c = (Curve) enum_.nextElement();
175 if (c.getOrder() > 0) {
176 edges.add(new Edge(c, curvetag));
177 }
178 }
179 }
180
181 private static Comparator YXTopComparator = new Comparator() {
182 public int compare(Object o1, Object o2) {
183 Curve c1 = ((Edge) o1).getCurve();
184 Curve c2 = ((Edge) o2).getCurve();
185 double v1, v2;
186 if ((v1 = c1.getYTop()) == (v2 = c2.getYTop())) {
187 if ((v1 = c1.getXTop()) == (v2 = c2.getXTop())) {
188 return 0;
189 }
190 }
191 if (v1 < v2) {
192 return -1;
193 }
194 return 1;
195 }
196 };
197
198 private Vector pruneEdges(Vector edges) {
199 int numedges = edges.size();
200 if (numedges < 2) {
201 return edges;
202 }
203 Edge[] edgelist = (Edge[]) edges.toArray(new Edge[numedges]);
204 Arrays.sort(edgelist, YXTopComparator);
205 if (false) {
206 System.out.println("pruning: ");
207 for (int i = 0; i < numedges; i++) {
208 System.out.println("edgelist["+i+"] = "+edgelist[i]);
209 }
210 }
211 Edge e;
212 int left = 0;
213 int right = 0;
214 int cur = 0;
215 int next = 0;
216 double yrange[] = new double[2];
217 Vector subcurves = new Vector();
218 Vector chains = new Vector();
219 Vector links = new Vector();
220 // Active edges are between left (inclusive) and right (exclusive)
221 while (left < numedges) {
222 double y = yrange[0];
223 // Prune active edges that fall off the top of the active y range
224 for (cur = next = right - 1; cur >= left; cur--) {
225 e = edgelist[cur];
226 if (e.getCurve().getYBot() > y) {
227 if (next > cur) {
228 edgelist[next] = e;
229 }
230 next--;
231 }
232 }
233 left = next + 1;
234 // Grab a new "top of Y range" if the active edges are empty
235 if (left >= right) {
236 if (right >= numedges) {
237 break;
238 }
239 y = edgelist[right].getCurve().getYTop();
240 if (y > yrange[0]) {
241 finalizeSubCurves(subcurves, chains);
242 }
243 yrange[0] = y;
244 }
245 // Incorporate new active edges that enter the active y range
246 while (right < numedges) {
247 e = edgelist[right];
248 if (e.getCurve().getYTop() > y) {
249 break;
250 }
251 right++;
252 }
253 // Sort the current active edges by their X values and
254 // determine the maximum valid Y range where the X ordering
255 // is correct
256 yrange[1] = edgelist[left].getCurve().getYBot();
257 if (right < numedges) {
258 y = edgelist[right].getCurve().getYTop();
259 if (yrange[1] > y) {
260 yrange[1] = y;
261 }
262 }
263 if (false) {
264 System.out.println("current line: y = ["+
265 yrange[0]+", "+yrange[1]+"]");
266 for (cur = left; cur < right; cur++) {
267 System.out.println(" "+edgelist[cur]);
268 }
269 }
270 // Note: We could start at left+1, but we need to make
271 // sure that edgelist[left] has its equivalence set to 0.
272 int nexteq = 1;
273 for (cur = left; cur < right; cur++) {
274 e = edgelist[cur];
275 e.setEquivalence(0);
276 for (next = cur; next > left; next--) {
277 Edge prevedge = edgelist[next-1];
278 int ordering = e.compareTo(prevedge, yrange);
279 if (yrange[1] <= yrange[0]) {
280 throw new InternalError("backstepping to "+yrange[1]+
281 " from "+yrange[0]);
282 }
283 if (ordering >= 0) {
284 if (ordering == 0) {
285 // If the curves are equal, mark them to be
286 // deleted later if they cancel each other
287 // out so that we avoid having extraneous
288 // curve segments.
289 int eq = prevedge.getEquivalence();
290 if (eq == 0) {
291 eq = nexteq++;
292 prevedge.setEquivalence(eq);
293 }
294 e.setEquivalence(eq);
295 }
296 break;
297 }
298 edgelist[next] = prevedge;
299 }
300 edgelist[next] = e;
301 }
302 if (false) {
303 System.out.println("current sorted line: y = ["+
304 yrange[0]+", "+yrange[1]+"]");
305 for (cur = left; cur < right; cur++) {
306 System.out.println(" "+edgelist[cur]);
307 }
308 }
309 // Now prune the active edge list.
310 // For each edge in the list, determine its classification
311 // (entering shape, exiting shape, ignore - no change) and
312 // record the current Y range and its classification in the
313 // Edge object for use later in constructing the new outline.
314 newRow();
315 double ystart = yrange[0];
316 double yend = yrange[1];
317 for (cur = left; cur < right; cur++) {
318 e = edgelist[cur];
319 int etag;
320 int eq = e.getEquivalence();
321 if (eq != 0) {
322 // Find one of the segments in the "equal" range
323 // with the right transition state and prefer an
324 // edge that was either active up until ystart
325 // or the edge that extends the furthest downward
326 // (i.e. has the most potential for continuation)
327 int origstate = getState();
328 etag = (origstate == AreaOp.RSTAG_INSIDE
329 ? AreaOp.ETAG_EXIT
330 : AreaOp.ETAG_ENTER);
331 Edge activematch = null;
332 Edge longestmatch = e;
333 double furthesty = yend;
334 do {
335 // Note: classify() must be called
336 // on every edge we consume here.
337 classify(e);
338 if (activematch == null &&
339 e.isActiveFor(ystart, etag))
340 {
341 activematch = e;
342 }
343 y = e.getCurve().getYBot();
344 if (y > furthesty) {
345 longestmatch = e;
346 furthesty = y;
347 }
348 } while (++cur < right &&
349 (e = edgelist[cur]).getEquivalence() == eq);
350 --cur;
351 if (getState() == origstate) {
352 etag = AreaOp.ETAG_IGNORE;
353 } else {
354 e = (activematch != null ? activematch : longestmatch);
355 }
356 } else {
357 etag = classify(e);
358 }
359 if (etag != AreaOp.ETAG_IGNORE) {
360 e.record(yend, etag);
361 links.add(new CurveLink(e.getCurve(), ystart, yend, etag));
362 }
363 }
364 // assert(getState() == AreaOp.RSTAG_OUTSIDE);
365 if (getState() != AreaOp.RSTAG_OUTSIDE) {
366 System.out.println("Still inside at end of active edge list!");
367 System.out.println("num curves = "+(right-left));
368 System.out.println("num links = "+links.size());
369 System.out.println("y top = "+yrange[0]);
370 if (right < numedges) {
371 System.out.println("y top of next curve = "+
372 edgelist[right].getCurve().getYTop());
373 } else {
374 System.out.println("no more curves");
375 }
376 for (cur = left; cur < right; cur++) {
377 e = edgelist[cur];
378 System.out.println(e);
379 int eq = e.getEquivalence();
380 if (eq != 0) {
381 System.out.println(" was equal to "+eq+"...");
382 }
383 }
384 }
385 if (false) {
386 System.out.println("new links:");
387 for (int i = 0; i < links.size(); i++) {
388 CurveLink link = (CurveLink) links.elementAt(i);
389 System.out.println(" "+link.getSubCurve());
390 }
391 }
392 resolveLinks(subcurves, chains, links);
393 links.clear();
394 // Finally capture the bottom of the valid Y range as the top
395 // of the next Y range.
396 yrange[0] = yend;
397 }
398 finalizeSubCurves(subcurves, chains);
399 Vector ret = new Vector();
400 Enumeration enum_ = subcurves.elements();
401 while (enum_.hasMoreElements()) {
402 CurveLink link = (CurveLink) enum_.nextElement();
403 ret.add(link.getMoveto());
404 CurveLink nextlink = link;
405 while ((nextlink = nextlink.getNext()) != null) {
406 if (!link.absorb(nextlink)) {
407 ret.add(link.getSubCurve());
408 link = nextlink;
409 }
410 }
411 ret.add(link.getSubCurve());
412 }
413 return ret;
414 }
415
416 public static void finalizeSubCurves(Vector subcurves, Vector chains) {
417 int numchains = chains.size();
418 if (numchains == 0) {
419 return;
420 }
421 if ((numchains & 1) != 0) {
422 throw new InternalError("Odd number of chains!");
423 }
424 ChainEnd[] endlist = new ChainEnd[numchains];
425 chains.toArray(endlist);
426 for (int i = 1; i < numchains; i += 2) {
427 ChainEnd open = endlist[i - 1];
428 ChainEnd close = endlist[i];
429 CurveLink subcurve = open.linkTo(close);
430 if (subcurve != null) {
431 subcurves.add(subcurve);
432 }
433 }
434 chains.clear();
435 }
436
437 private static CurveLink[] EmptyLinkList = new CurveLink[2];
438 private static ChainEnd[] EmptyChainList = new ChainEnd[2];
439
440 public static void resolveLinks(Vector subcurves,
441 Vector chains,
442 Vector links)
443 {
444 int numlinks = links.size();
445 CurveLink[] linklist;
446 if (numlinks == 0) {
447 linklist = EmptyLinkList;
448 } else {
449 if ((numlinks & 1) != 0) {
450 throw new InternalError("Odd number of new curves!");
451 }
452 linklist = new CurveLink[numlinks+2];
453 links.toArray(linklist);
454 }
455 int numchains = chains.size();
456 ChainEnd[] endlist;
457 if (numchains == 0) {
458 endlist = EmptyChainList;
459 } else {
460 if ((numchains & 1) != 0) {
461 throw new InternalError("Odd number of chains!");
462 }
463 endlist = new ChainEnd[numchains+2];
464 chains.toArray(endlist);
465 }
466 int curchain = 0;
467 int curlink = 0;
468 chains.clear();
469 ChainEnd chain = endlist[0];
470 ChainEnd nextchain = endlist[1];
471 CurveLink link = linklist[0];
472 CurveLink nextlink = linklist[1];
473 while (chain != null || link != null) {
474 /*
475 * Strategy 1:
476 * Connect chains or links if they are the only things left...
477 */
478 boolean connectchains = (link == null);
479 boolean connectlinks = (chain == null);
480
481 if (!connectchains && !connectlinks) {
482 // assert(link != null && chain != null);
483 /*
484 * Strategy 2:
485 * Connect chains or links if they close off an open area...
486 */
487 connectchains = ((curchain & 1) == 0 &&
488 chain.getX() == nextchain.getX());
489 connectlinks = ((curlink & 1) == 0 &&
490 link.getX() == nextlink.getX());
491
492 if (!connectchains && !connectlinks) {
493 /*
494 * Strategy 3:
495 * Connect chains or links if their successor is
496 * between them and their potential connectee...
497 */
498 double cx = chain.getX();
499 double lx = link.getX();
500 connectchains =
501 (nextchain != null && cx < lx &&
502 obstructs(nextchain.getX(), lx, curchain));
503 connectlinks =
504 (nextlink != null && lx < cx &&
505 obstructs(nextlink.getX(), cx, curlink));
506 }
507 }
508 if (connectchains) {
509 CurveLink subcurve = chain.linkTo(nextchain);
510 if (subcurve != null) {
511 subcurves.add(subcurve);
512 }
513 curchain += 2;
514 chain = endlist[curchain];
515 nextchain = endlist[curchain+1];
516 }
517 if (connectlinks) {
518 ChainEnd openend = new ChainEnd(link, null);
519 ChainEnd closeend = new ChainEnd(nextlink, openend);
520 openend.setOtherEnd(closeend);
521 chains.add(openend);
522 chains.add(closeend);
523 curlink += 2;
524 link = linklist[curlink];
525 nextlink = linklist[curlink+1];
526 }
527 if (!connectchains && !connectlinks) {
528 // assert(link != null);
529 // assert(chain != null);
530 // assert(chain.getEtag() == link.getEtag());
531 chain.addLink(link);
532 chains.add(chain);
533 curchain++;
534 chain = nextchain;
535 nextchain = endlist[curchain+1];
536 curlink++;
537 link = nextlink;
538 nextlink = linklist[curlink+1];
539 }
540 }
541 if ((chains.size() & 1) != 0) {
542 System.out.println("Odd number of chains!");
543 }
544 }
545
546 /*
547 * Does the position of the next edge at v1 "obstruct" the
548 * connectivity between current edge and the potential
549 * partner edge which is positioned at v2?
550 *
551 * Phase tells us whether we are testing for a transition
552 * into or out of the interior part of the resulting area.
553 *
554 * Require 4-connected continuity if this edge and the partner
555 * edge are both "entering into" type edges
556 * Allow 8-connected continuity for "exiting from" type edges
557 */
558 public static boolean obstructs(double v1, double v2, int phase) {
559 return (((phase & 1) == 0) ? (v1 <= v2) : (v1 < v2));
560 }
561}