/[classpath]/classpath/java/awt/geom/Area.java
ViewVC logotype

Diff of /classpath/java/awt/geom/Area.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by ericb, Fri Mar 22 16:54:31 2002 UTC revision 1.1.2.1 by gnu_andrew, Thu Jan 13 22:40:38 2005 UTC
# Line 1  Line 1 
1  /* Area.java -- represents a shape built by constructive area geometry  /* Area.java -- represents a shape built by constructive area geometry
2     Copyright (C) 2002 Free Software Foundation     Copyright (C) 2002, 2004 Free Software Foundation
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 35  this exception to your version of the li Line 35  this exception to your version of the li
35  obligated to do so.  If you do not wish to do so, delete this  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */  exception statement from your version. */
37    
   
38  package java.awt.geom;  package java.awt.geom;
39    
40  import java.awt.Rectangle;  import java.awt.Rectangle;
41  import java.awt.Shape;  import java.awt.Shape;
42    import java.util.Vector;
43    
44    
45  /**  /**
46   * STUBS ONLY   * The Area class represents any area for the purpose of
47   * XXX Implement and document.   * Constructive Area Geometry (CAG) manipulations. CAG manipulations
48     * work as an area-wise form of boolean logic, where the basic operations are:
49     * <P><li>Add (in boolean algebra: A <B>or</B> B)<BR>
50     * <li>Subtract (in boolean algebra: A <B>and</B> (<B>not</B> B) )<BR>
51     * <li>Intersect (in boolean algebra: A <B>and</B> B)<BR>
52     * <li>Exclusive Or <BR>
53     * <img src="doc-files/Area-1.png" width="342" height="302"
54     * alt="Illustration of CAG operations" /><BR>
55     * Above is an illustration of the CAG operations on two ring shapes.<P>
56     *
57     * The contains and intersects() methods are also more accurate than the
58     * specification of #Shape requires.<P>
59     *
60     * Please note that constructing an Area can be slow
61     * (Self-intersection resolving is proportional to the square of
62     * the number of segments).<P>
63     * @see #add(Area)
64     * @see #subtract(Area)
65     * @see #intersect(Area)
66     * @see #exclusiveOr(Area)
67     *
68     * @author Sven de Marothy (sven@physto.se)
69     *
70     * @since 1.2
71     * @status Works, but could be faster and more reliable.
72   */   */
73  public class Area implements Shape, Cloneable  public class Area implements Shape, Cloneable
74  {  {
75      /**
76       * General numerical precision
77       */
78      private final double EPSILON = 1E-11;
79    
80      /**
81       * recursive subdivision epsilon - (see getRecursionDepth)
82       */
83      private final double RS_EPSILON = 1E-13;
84    
85      /**
86       * Snap distance - points within this distance are considered equal
87       */
88      private final double PE_EPSILON = 1E-11;
89    
90      /**
91       * Segment vectors containing solid areas and holes
92       */
93      private Vector solids;
94    
95      /**
96       * Segment vectors containing solid areas and holes
97       */
98      private Vector holes;
99    
100      /**
101       * Vector (temporary) storing curve-curve intersections
102       */
103      private Vector cc_intersections;
104    
105      /**
106       * Winding rule WIND_NON_ZERO used, after construction,
107       * this is irrelevant.
108       */
109      private int windingRule;
110    
111      /**
112       * Constructs an empty Area
113       */
114    public Area()    public Area()
115    {    {
116        solids = new Vector();
117        holes = new Vector();
118    }    }
119    
120      /**
121       * Constructs an Area from any given Shape. <P>
122       *
123       * If the Shape is self-intersecting, the created Area will consist
124       * of non-self-intersecting subpaths, and any inner paths which
125       * are found redundant in accordance with the Shape's winding rule
126       * will not be included.
127       */
128    public Area(Shape s)    public Area(Shape s)
129    {    {
130        this();
131    
132        Vector p = makeSegment(s);
133    
134        // empty path
135        if (p == null)
136          return;
137    
138        // delete empty paths
139        for (int i = 0; i < p.size(); i++)
140          if (((Segment) p.elementAt(i)).getSignedArea() == 0.0)
141            p.remove(i--);
142    
143        /*
144         * Resolve self intersecting paths into non-intersecting
145         * solids and holes.
146         * Algorithm is as follows:
147         * 1: Create nodes at all self intersections
148         * 2: Put all segments into a list
149         * 3: Grab a segment, follow it, change direction at each node,
150         *    removing segments from the list in the process
151         * 4: Repeat (3) until no segments remain in the list
152         * 5: Remove redundant paths and sort into solids and holes
153         */
154        Vector paths = new Vector();
155        Segment v;
156    
157        for (int i = 0; i < p.size(); i++)
158          {
159            Segment path = (Segment) p.elementAt(i);
160            createNodesSelf(path);
161          }
162    
163        if (p.size() > 1)
164          {
165            for (int i = 0; i < p.size() - 1; i++)
166              for (int j = i + 1; j < p.size(); j++)
167                {
168                  Segment path1 = (Segment) p.elementAt(i);
169                  Segment path2 = (Segment) p.elementAt(j);
170                  createNodes(path1, path2);
171                }
172          }
173    
174        // we have intersecting points.
175        Vector segments = new Vector();
176    
177        for (int i = 0; i < p.size(); i++)
178          {
179            Segment path = v = (Segment) p.elementAt(i);
180            do
181              {
182                segments.add(v);
183                v = v.next;
184              }
185            while (v != path);
186          }
187    
188        paths = weilerAtherton(segments);
189        deleteRedundantPaths(paths);
190    }    }
191    public void add(Area a)  
192      /**
193       * Performs an add (union) operation on this area with another Area.<BR>
194       * @param area - the area to be unioned with this one
195       */
196      public void add(Area area)
197    {    {
198      // XXX Implement.      if (equals(area))
199      throw new Error("not implemented");        return;
200        if (area.isEmpty())
201          return;
202    
203        Area B = (Area) area.clone();
204    
205        Vector pathA = new Vector();
206        Vector pathB = new Vector();
207        pathA.addAll(solids);
208        pathA.addAll(holes);
209        pathB.addAll(B.solids);
210        pathB.addAll(B.holes);
211    
212        int nNodes = 0;
213    
214        for (int i = 0; i < pathA.size(); i++)
215          {
216            Segment a = (Segment) pathA.elementAt(i);
217            for (int j = 0; j < pathB.size(); j++)
218              {
219                Segment b = (Segment) pathB.elementAt(j);
220                nNodes += createNodes(a, b);
221              }
222          }
223    
224        Vector paths = new Vector();
225        Segment v;
226    
227        // we have intersecting points.
228        Vector segments = new Vector();
229    
230        // In a union operation, we keep all
231        // segments of A oustide B and all B outside A
232        for (int i = 0; i < pathA.size(); i++)
233          {
234            v = (Segment) pathA.elementAt(i);
235            Segment path = v;
236            do
237              {
238                if (v.isSegmentOutside(area))
239                  segments.add(v);
240                v = v.next;
241              }
242            while (v != path);
243          }
244    
245        for (int i = 0; i < pathB.size(); i++)
246          {
247            v = (Segment) pathB.elementAt(i);
248            Segment path = v;
249            do
250              {
251                if (v.isSegmentOutside(this))
252                  segments.add(v);
253                v = v.next;
254              }
255            while (v != path);
256          }
257    
258        paths = weilerAtherton(segments);
259        deleteRedundantPaths(paths);
260    }    }
261    public void subtract(Area a)  
262      /**
263       * Performs a subtraction operation on this Area.<BR>
264       * @param area - the area to be subtracted from this area.
265       */
266      public void subtract(Area area)
267    {    {
268      // XXX Implement.      if (isEmpty() || area.isEmpty())
269      throw new Error("not implemented");        return;
270    
271        if (equals(area))
272          {
273            reset();
274            return;
275          }
276    
277        Vector pathA = new Vector();
278        Area B = (Area) area.clone();
279        pathA.addAll(solids);
280        pathA.addAll(holes);
281    
282        // reverse the directions of B paths.
283        setDirection(B.holes, true);
284        setDirection(B.solids, false);
285    
286        Vector pathB = new Vector();
287        pathB.addAll(B.solids);
288        pathB.addAll(B.holes);
289    
290        int nNodes = 0;
291    
292        // create nodes
293        for (int i = 0; i < pathA.size(); i++)
294          {
295            Segment a = (Segment) pathA.elementAt(i);
296            for (int j = 0; j < pathB.size(); j++)
297              {
298                Segment b = (Segment) pathB.elementAt(j);
299                nNodes += createNodes(a, b);
300              }
301          }
302    
303        Vector paths = new Vector();
304    
305        // we have intersecting points.
306        Vector segments = new Vector();
307    
308        // In a subtraction operation, we keep all
309        // segments of A oustide B and all B within A
310        // We outsideness-test only one segment in each path
311        // and the segments before and after any node
312        for (int i = 0; i < pathA.size(); i++)
313          {
314            Segment v = (Segment) pathA.elementAt(i);
315            Segment path = v;
316            if (v.isSegmentOutside(area) && v.node == null)
317              segments.add(v);
318            boolean node = false;
319            do
320              {
321                if ((v.node != null || node))
322                  {
323                    node = (v.node != null);
324                    if (v.isSegmentOutside(area))
325                      segments.add(v);
326                  }
327                v = v.next;
328              }
329            while (v != path);
330          }
331    
332        for (int i = 0; i < pathB.size(); i++)
333          {
334            Segment v = (Segment) pathB.elementAt(i);
335            Segment path = v;
336            if (! v.isSegmentOutside(this) && v.node == null)
337              segments.add(v);
338            v = v.next;
339            boolean node = false;
340            do
341              {
342                if ((v.node != null || node))
343                  {
344                    node = (v.node != null);
345                    if (! v.isSegmentOutside(this))
346                      segments.add(v);
347                  }
348                v = v.next;
349              }
350            while (v != path);
351          }
352    
353        paths = weilerAtherton(segments);
354        deleteRedundantPaths(paths);
355    }    }
356    public void intersect(Area a)  
357      /**
358       * Performs an intersection operation on this Area.<BR>
359       * @param area - the area to be intersected with this area.
360       */
361      public void intersect(Area area)
362    {    {
363      // XXX Implement.      if (isEmpty() || area.isEmpty())
364      throw new Error("not implemented");        {
365            reset();
366            return;
367          }
368        if (equals(area))
369          return;
370    
371        Vector pathA = new Vector();
372        Area B = (Area) area.clone();
373        pathA.addAll(solids);
374        pathA.addAll(holes);
375    
376        Vector pathB = new Vector();
377        pathB.addAll(B.solids);
378        pathB.addAll(B.holes);
379    
380        int nNodes = 0;
381    
382        // create nodes
383        for (int i = 0; i < pathA.size(); i++)
384          {
385            Segment a = (Segment) pathA.elementAt(i);
386            for (int j = 0; j < pathB.size(); j++)
387              {
388                Segment b = (Segment) pathB.elementAt(j);
389                nNodes += createNodes(a, b);
390              }
391          }
392    
393        Vector paths = new Vector();
394    
395        // we have intersecting points.
396        Vector segments = new Vector();
397    
398        // In an intersection operation, we keep all
399        // segments of A within B and all B within A
400        // (The rest must be redundant)
401        // We outsideness-test only one segment in each path
402        // and the segments before and after any node
403        for (int i = 0; i < pathA.size(); i++)
404          {
405            Segment v = (Segment) pathA.elementAt(i);
406            Segment path = v;
407            if (! v.isSegmentOutside(area) && v.node == null)
408              segments.add(v);
409            boolean node = false;
410            do
411              {
412                if ((v.node != null || node))
413                  {
414                    node = (v.node != null);
415                    if (! v.isSegmentOutside(area))
416                      segments.add(v);
417                  }
418                v = v.next;
419              }
420            while (v != path);
421          }
422    
423        for (int i = 0; i < pathB.size(); i++)
424          {
425            Segment v = (Segment) pathB.elementAt(i);
426            Segment path = v;
427            if (! v.isSegmentOutside(this) && v.node == null)
428              segments.add(v);
429            v = v.next;
430            boolean node = false;
431            do
432              {
433                if ((v.node != null || node))
434                  {
435                    node = (v.node != null);
436                    if (! v.isSegmentOutside(this))
437                      segments.add(v);
438                  }
439                v = v.next;
440              }
441            while (v != path);
442          }
443    
444        paths = weilerAtherton(segments);
445        deleteRedundantPaths(paths);
446    }    }
447    public void exclusiveOr(Area a)  
448      /**
449       * Performs an exclusive-or operation on this Area.<BR>
450       * @param area - the area to be XORed with this area.
451       */
452      public void exclusiveOr(Area area)
453    {    {
454      // XXX Implement.      if (area.isEmpty())
455      throw new Error("not implemented");        return;
456    
457        if (isEmpty())
458          {
459            Area B = (Area) area.clone();
460            solids = B.solids;
461            holes = B.holes;
462            return;
463          }
464        if (equals(area))
465          {
466            reset();
467            return;
468          }
469    
470        Vector pathA = new Vector();
471    
472        Area B = (Area) area.clone();
473        Vector pathB = new Vector();
474        pathA.addAll(solids);
475        pathA.addAll(holes);
476    
477        // reverse the directions of B paths.
478        setDirection(B.holes, true);
479        setDirection(B.solids, false);
480        pathB.addAll(B.solids);
481        pathB.addAll(B.holes);
482    
483        int nNodes = 0;
484    
485        for (int i = 0; i < pathA.size(); i++)
486          {
487            Segment a = (Segment) pathA.elementAt(i);
488            for (int j = 0; j < pathB.size(); j++)
489              {
490                Segment b = (Segment) pathB.elementAt(j);
491                nNodes += createNodes(a, b);
492              }
493          }
494    
495        Vector paths = new Vector();
496        Segment v;
497    
498        // we have intersecting points.
499        Vector segments = new Vector();
500    
501        // In an XOR operation, we operate on all segments
502        for (int i = 0; i < pathA.size(); i++)
503          {
504            v = (Segment) pathA.elementAt(i);
505            Segment path = v;
506            do
507              {
508                segments.add(v);
509                v = v.next;
510              }
511            while (v != path);
512          }
513    
514        for (int i = 0; i < pathB.size(); i++)
515          {
516            v = (Segment) pathB.elementAt(i);
517            Segment path = v;
518            do
519              {
520                segments.add(v);
521                v = v.next;
522              }
523            while (v != path);
524          }
525    
526        paths = weilerAtherton(segments);
527        deleteRedundantPaths(paths);
528    }    }
529    
530      /**
531       * Clears the Area object, creating an empty area.
532       */
533    public void reset()    public void reset()
534    {    {
535      // XXX Implement.      solids = new Vector();
536      throw new Error("not implemented");      holes = new Vector();
537    }    }
538    
539      /**
540       * Returns whether this area encloses any area.
541       * @return true if the object encloses any area.
542       */
543    public boolean isEmpty()    public boolean isEmpty()
544    {    {
545      // XXX Implement.      if (solids.size() == 0)
546      throw new Error("not implemented");        return true;
547    
548        double totalArea = 0;
549        for (int i = 0; i < solids.size(); i++)
550          totalArea += Math.abs(((Segment) solids.elementAt(i)).getSignedArea());
551        for (int i = 0; i < holes.size(); i++)
552          totalArea -= Math.abs(((Segment) holes.elementAt(i)).getSignedArea());
553        if (totalArea <= EPSILON)
554          return true;
555    
556        return false;
557    }    }
558    
559      /**
560       * Determines whether the Area consists entirely of line segments
561       * @return true if the Area lines-only, false otherwise
562       */
563    public boolean isPolygonal()    public boolean isPolygonal()
564    {    {
565      // XXX Implement.      for (int i = 0; i < holes.size(); i++)
566      throw new Error("not implemented");        if (! ((Segment) holes.elementAt(i)).isPolygonal())
567            return false;
568        for (int i = 0; i < solids.size(); i++)
569          if (! ((Segment) solids.elementAt(i)).isPolygonal())
570            return false;
571        return true;
572    }    }
573    
574      /**
575       * Determines if the Area is rectangular.<P>
576       *
577       * This is strictly qualified. An area is considered rectangular if:<BR>
578       * <li>It consists of a single polygonal path.<BR>
579       * <li>It is oriented parallel/perpendicular to the xy axis<BR>
580       * <li>It must be exactly rectangular, i.e. small errors induced by
581       * transformations may cause a false result, although the area is
582       * visibly rectangular.<P>
583       * @return true if the above criteria are met, false otherwise
584       */
585    public boolean isRectangular()    public boolean isRectangular()
586    {    {
587      // XXX Implement.      if (holes.size() != 0 || solids.size() != 1)
588      throw new Error("not implemented");        return false;
589    
590        Segment path = (Segment) solids.elementAt(0);
591        if (! path.isPolygonal())
592          return false;
593    
594        int nCorners = 0;
595        Segment s = path;
596        do
597          {
598            Segment s2 = s.next;
599            double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/
600                ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
601            double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/
602                ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
603            double dotproduct = d1 + d2;
604    
605            // For some reason, only rectangles on the XY axis count.
606            if (d1 != 0 && d2 != 0)
607              return false;
608    
609            if (Math.abs(dotproduct) == 0) // 90 degree angle
610              nCorners++;
611            else if ((Math.abs(1.0 - dotproduct) > 0)) // 0 degree angle?
612              return false; // if not, return false
613    
614            s = s.next;
615          }
616        while (s != path);
617    
618        return nCorners == 4;
619    }    }
620    
621      /**
622       * Returns whether the Area consists of more than one simple
623       * (non self-intersecting) subpath.
624       *
625       * @return true if the Area consists of none or one simple subpath,
626       * false otherwise.
627       */
628    public boolean isSingular()    public boolean isSingular()
629    {    {
630      // XXX Implement.      return (holes.size() == 0 && solids.size() <= 1);
     throw new Error("not implemented");  
631    }    }
632    
633      /**
634       * Returns the bounding box of the Area.<P> Unlike the CubicCurve2D and
635       * QuadraticCurve2D classes, this method will return the tightest possible
636       * bounding box, evaluating the extreme points of each curved segment.<P>
637       * @return the bounding box
638       */
639    public Rectangle2D getBounds2D()    public Rectangle2D getBounds2D()
640    {    {
641      // XXX Implement.      if (solids.size() == 0)
642      throw new Error("not implemented");        return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
643    
644        double xmin;
645        double xmax;
646        double ymin;
647        double ymax;
648        xmin = xmax = ((Segment) solids.elementAt(0)).P1.getX();
649        ymin = ymax = ((Segment) solids.elementAt(0)).P1.getY();
650    
651        for (int path = 0; path < solids.size(); path++)
652          {
653            Rectangle2D r = ((Segment) solids.elementAt(path)).getPathBounds();
654            xmin = Math.min(r.getMinX(), xmin);
655            ymin = Math.min(r.getMinY(), ymin);
656            xmax = Math.max(r.getMaxX(), xmax);
657            ymax = Math.max(r.getMaxY(), ymax);
658          }
659    
660        return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
661    }    }
662    
663      /**
664       * Returns the bounds of this object in Rectangle format.
665       * Please note that this may lead to loss of precision.
666       * @see #getBounds2D()
667       */
668    public Rectangle getBounds()    public Rectangle getBounds()
669    {    {
670      return getBounds2D().getBounds();      return getBounds2D().getBounds();
# Line 118  public class Area implements Shape, Clon Line 680  public class Area implements Shape, Clon
680    {    {
681      try      try
682        {        {
683          return super.clone();          Area clone = new Area();
684            for (int i = 0; i < solids.size(); i++)
685              clone.solids.add(((Segment) solids.elementAt(i)).cloneSegmentList());
686            for (int i = 0; i < holes.size(); i++)
687              clone.holes.add(((Segment) holes.elementAt(i)).cloneSegmentList());
688            return clone;
689        }        }
690      catch (CloneNotSupportedException e)      catch (CloneNotSupportedException e)
691        {        {
692          throw (Error) new InternalError().initCause(e); // Impossible          throw (Error) new InternalError().initCause(e); // Impossible
693        }        }
694    }    }
695    
696    public boolean equals(Area a)    /**
697       * Compares two Areas.
698       *
699       * @return true if the areas are equal. False otherwise.
700       */
701      public boolean equals(Area area)
702    {    {
703      // XXX Implement.      if (! getBounds2D().equals(area.getBounds2D()))
704      throw new Error("not implemented");        return false;
705    
706        if (solids.size() != area.solids.size()
707            || holes.size() != area.holes.size())
708          return false;
709    
710        Vector pathA = new Vector();
711        pathA.addAll(solids);
712        pathA.addAll(holes);
713        Vector pathB = new Vector();
714        pathB.addAll(area.solids);
715        pathB.addAll(area.holes);
716    
717        int nPaths = pathA.size();
718        boolean[][] match = new boolean[2][nPaths];
719    
720        for (int i = 0; i < nPaths; i++)
721          {
722            for (int j = 0; j < nPaths; j++)
723              {
724                Segment p1 = (Segment) pathA.elementAt(i);
725                Segment p2 = (Segment) pathB.elementAt(j);
726                if (! match[0][i] && ! match[1][j])
727                  if (p1.pathEquals(p2))
728                    match[0][i] = match[1][j] = true;
729              }
730          }
731    
732        boolean result = true;
733        for (int i = 0; i < nPaths; i++)
734          result = result && match[0][i] && match[1][i];
735        return result;
736    }    }
737    
738      /**
739       * Transforms this area by the AffineTransform at
740       */
741    public void transform(AffineTransform at)    public void transform(AffineTransform at)
742    {    {
743      // XXX Implement.      for (int i = 0; i < solids.size(); i++)
744      throw new Error("not implemented");        ((Segment) solids.elementAt(i)).transformSegmentList(at);
745        for (int i = 0; i < holes.size(); i++)
746          ((Segment) holes.elementAt(i)).transformSegmentList(at);
747    
748        // Note that the orientation is not invariant under inversion
749        if ((at.getType() & AffineTransform.TYPE_FLIP) != 0)
750          {
751            setDirection(holes, false);
752            setDirection(solids, true);
753          }
754    }    }
755    
756      /**
757       * Returns a new Area equal to this one, transformed
758       * by the AffineTransform at
759       * @return the transformed area
760       */
761    public Area createTransformedArea(AffineTransform at)    public Area createTransformedArea(AffineTransform at)
762    {    {
763      Area a = (Area) clone();      Area a = (Area) clone();
764      a.transform(at);      a.transform(at);
765      return a;      return a;
766    }    }
767    
768      /**
769       * Determines if the point (x,y) is contained within this Area.
770       *
771       * @return true if the point is contained, false otherwise.
772       */
773    public boolean contains(double x, double y)    public boolean contains(double x, double y)
774    {    {
775      // XXX Implement.      int n = 0;
776      throw new Error("not implemented");      for (int i = 0; i < solids.size(); i++)
777          if (((Segment) solids.elementAt(i)).contains(x, y))
778            n++;
779    
780        for (int i = 0; i < holes.size(); i++)
781          if (((Segment) holes.elementAt(i)).contains(x, y))
782            n--;
783    
784        return (n != 0);
785    }    }
786    
787      /**
788       * Determines if the Point2D p is contained within this Area.
789       *
790       * @return true if the point is contained, false otherwise.
791       */
792    public boolean contains(Point2D p)    public boolean contains(Point2D p)
793    {    {
794      return contains(p.getX(), p.getY());      return contains(p.getX(), p.getY());
795    }    }
796    
797      /**
798       * Determines if the rectangle specified by (x,y) as the upper-left
799       * and with width w and height h is completely contained within this Area,
800       * returns false otherwise.<P>
801       *
802       * This method should always produce the correct results, unlike for other
803       * classes in geom.
804       * @return true if the rectangle is considered contained
805       */
806    public boolean contains(double x, double y, double w, double h)    public boolean contains(double x, double y, double w, double h)
807    {    {
808      // XXX Implement.      LineSegment[] l = new LineSegment[4];
809      throw new Error("not implemented");      l[0] = new LineSegment(x, y, x + w, y);
810        l[1] = new LineSegment(x, y + h, x + w, y + h);
811        l[2] = new LineSegment(x, y, x, y + h);
812        l[3] = new LineSegment(x + w, y, x + w, y + h);
813    
814        // Since every segment in the area must a contour
815        // between inside/outside segments, ANY intersection
816        // will mean the rectangle is not entirely contained.
817        for (int i = 0; i < 4; i++)
818          {
819            for (int path = 0; path < solids.size(); path++)
820              {
821                Segment v;
822                Segment start;
823                start = v = (Segment) solids.elementAt(path);
824                do
825                  {
826                    if (l[i].hasIntersections(v))
827                      return false;
828                    v = v.next;
829                  }
830                while (v != start);
831              }
832            for (int path = 0; path < holes.size(); path++)
833              {
834                Segment v;
835                Segment start;
836                start = v = (Segment) holes.elementAt(path);
837                do
838                  {
839                    if (l[i].hasIntersections(v))
840                      return false;
841                    v = v.next;
842                  }
843                while (v != start);
844              }
845          }
846    
847        // Is any point inside?
848        if (! contains(x, y))
849          return false;
850    
851        // Final hoop: Is the rectangle non-intersecting and inside,
852        // but encloses a hole?
853        Rectangle2D r = new Rectangle2D.Double(x, y, w, h);
854        for (int path = 0; path < holes.size(); path++)
855          if (! ((Segment) holes.elementAt(path)).isSegmentOutside(r))
856            return false;
857    
858        return true;
859    }    }
860    
861      /**
862       * Determines if the Rectangle2D specified by r is completely contained
863       * within this Area, returns false otherwise.<P>
864       *
865       * This method should always produce the correct results, unlike for other
866       * classes in geom.
867       * @return true if the rectangle is considered contained
868       */
869    public boolean contains(Rectangle2D r)    public boolean contains(Rectangle2D r)
870    {    {
871      return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());      return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
872    }    }
873    
874      /**
875       * Determines if the rectangle specified by (x,y) as the upper-left
876       * and with width w and height h intersects any part of this Area.
877       * @return true if the rectangle intersects the area, false otherwise.
878       */
879    public boolean intersects(double x, double y, double w, double h)    public boolean intersects(double x, double y, double w, double h)
880    {    {
881      // XXX Implement.      if (solids.size() == 0)
882      throw new Error("not implemented");        return false;
883    
884        LineSegment[] l = new LineSegment[4];
885        l[0] = new LineSegment(x, y, x + w, y);
886        l[1] = new LineSegment(x, y + h, x + w, y + h);
887        l[2] = new LineSegment(x, y, x, y + h);
888        l[3] = new LineSegment(x + w, y, x + w, y + h);
889    
890        // Return true on any intersection
891        for (int i = 0; i < 4; i++)
892          {
893            for (int path = 0; path < solids.size(); path++)
894              {
895                Segment v;
896                Segment start;
897                start = v = (Segment) solids.elementAt(path);
898                do
899                  {
900                    if (l[i].hasIntersections(v))
901                      return true;
902                    v = v.next;
903                  }
904                while (v != start);
905              }
906            for (int path = 0; path < holes.size(); path++)
907              {
908                Segment v;
909                Segment start;
910                start = v = (Segment) holes.elementAt(path);
911                do
912                  {
913                    if (l[i].hasIntersections(v))
914                      return true;
915                    v = v.next;
916                  }
917                while (v != start);
918              }
919          }
920    
921        // Non-intersecting, Is any point inside?
922        if (contains(x, y))
923          return true;
924    
925        // What if the rectangle encloses the whole shape?
926        Point2D p = ((Segment) solids.elementAt(0)).getMidPoint();
927        if ((new Rectangle2D.Double(x, y, w, h)).contains(p))
928          return true;
929        return false;
930    }    }
931    
932      /**
933       * Determines if the Rectangle2D specified by r intersects any
934       * part of this Area.
935       * @return true if the rectangle intersects the area, false otherwise.
936       */
937    public boolean intersects(Rectangle2D r)    public boolean intersects(Rectangle2D r)
938    {    {
939      return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());      return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
940    }    }
941    
942      /**
943       * Returns a PathIterator object defining the contour of this Area,
944       * transformed by at.
945       */
946    public PathIterator getPathIterator(AffineTransform at)    public PathIterator getPathIterator(AffineTransform at)
947    {    {
948      // XXX Implement.      return (new AreaIterator(at));
     throw new Error("not implemented");  
949    }    }
950    
951      //---------------------------------------------------------------------
952      // Non-public methods and classes
953    
954      /**
955       * Returns a flattened PathIterator object defining the contour of this
956       * Area, transformed by at and with a defined flatness.
957       */
958    public PathIterator getPathIterator(AffineTransform at, double flatness)    public PathIterator getPathIterator(AffineTransform at, double flatness)
959    {    {
960      return new FlatteningPathIterator(getPathIterator(at), flatness);      return new FlatteningPathIterator(getPathIterator(at), flatness);
961    }    }
962    
963      /**
964       * Private pathiterator object.
965       */
966      private class AreaIterator implements PathIterator
967      {
968        private Vector segments;
969        private int index;
970        private AffineTransform at;
971    
972        // Simple compound type for segments
973        class IteratorSegment
974        {
975          int type;
976          double[] coords;
977    
978          IteratorSegment()
979          {
980            coords = new double[6];
981          }
982        }
983    
984        /**
985         * The contructor here does most of the work,
986         * creates a vector of IteratorSegments, which can
987         * readily be returned
988         */
989        public AreaIterator(AffineTransform at)
990        {
991          this.at = at;
992          index = 0;
993          segments = new Vector();
994          Vector allpaths = new Vector();
995          allpaths.addAll(solids);
996          allpaths.addAll(holes);
997    
998          for (int i = 0; i < allpaths.size(); i++)
999            {
1000              Segment v = (Segment) allpaths.elementAt(i);
1001              Segment start = v;
1002    
1003              IteratorSegment is = new IteratorSegment();
1004              is.type = SEG_MOVETO;
1005              is.coords[0] = start.P1.getX();
1006              is.coords[1] = start.P1.getY();
1007              segments.add(is);
1008    
1009              do
1010                {
1011                  is = new IteratorSegment();
1012                  is.type = v.pathIteratorFormat(is.coords);
1013                  segments.add(is);
1014                  v = v.next;
1015                }
1016              while (v != start);
1017    
1018              is = new IteratorSegment();
1019              is.type = SEG_CLOSE;
1020              segments.add(is);
1021            }
1022        }
1023    
1024        public int currentSegment(double[] coords)
1025        {
1026          IteratorSegment s = (IteratorSegment) segments.elementAt(index);
1027          if (at != null)
1028            at.transform(s.coords, 0, coords, 0, 3);
1029          else
1030            for (int i = 0; i < 6; i++)
1031              coords[i] = s.coords[i];
1032          return (s.type);
1033        }
1034    
1035        public int currentSegment(float[] coords)
1036        {
1037          IteratorSegment s = (IteratorSegment) segments.elementAt(index);
1038          double[] d = new double[6];
1039          if (at != null)
1040            {
1041              at.transform(s.coords, 0, d, 0, 3);
1042              for (int i = 0; i < 6; i++)
1043                coords[i] = (float) d[i];
1044            }
1045          else
1046            for (int i = 0; i < 6; i++)
1047              coords[i] = (float) s.coords[i];
1048          return (s.type);
1049        }
1050    
1051        // Note that the winding rule should not matter here,
1052        // EVEN_ODD is chosen because it renders faster.
1053        public int getWindingRule()
1054        {
1055          return (PathIterator.WIND_EVEN_ODD);
1056        }
1057    
1058        public boolean isDone()
1059        {
1060          return (index >= segments.size());
1061        }
1062    
1063        public void next()
1064        {
1065          index++;
1066        }
1067      }
1068    
1069      /**
1070       * Performs the fundamental task of the Weiler-Atherton algorithm,
1071       * traverse a list of segments, for each segment:
1072       * Follow it, removing segments from the list and switching paths
1073       * at each node. Do so until the starting segment is reached.
1074       *
1075       * Returns a Vector of the resulting paths.
1076       */
1077      private Vector weilerAtherton(Vector segments)
1078      {
1079        Vector paths = new Vector();
1080        while (segments.size() > 0)
1081          {
1082            // Iterate over the path
1083            Segment start = (Segment) segments.elementAt(0);
1084            Segment s = start;
1085            do
1086              {
1087                segments.remove(s);
1088                if (s.node != null)
1089                  { // switch over
1090                    s.next = s.node;
1091                    s.node = null;
1092                  }
1093                s = s.next; // continue
1094              }
1095            while (s != start);
1096    
1097            paths.add(start);
1098          }
1099        return paths;
1100      }
1101    
1102      /**
1103       * A small wrapper class to store intersection points
1104       */
1105      private class Intersection
1106      {
1107        Point2D p; // the 2D point of intersection
1108        double ta; // the parametric value on a
1109        double tb; // the parametric value on b
1110        Segment seg; // segment placeholder for node setting
1111    
1112        public Intersection(Point2D p, double ta, double tb)
1113        {
1114          this.p = p;
1115          this.ta = ta;
1116          this.tb = tb;
1117        }
1118      }
1119    
1120      /**
1121       * Returns the recursion depth necessary to approximate the
1122       * curve by line segments within the error RS_EPSILON.
1123       *
1124       * This is done with Wang's formula:
1125       * L0 = max{0<=i<=N-2}(|xi - 2xi+1 + xi+2|,|yi - 2yi+1 + yi+2|)
1126       * r0 = log4(sqrt(2)*N*(N-1)*L0/8e)
1127       * Where e is the maximum distance error (RS_EPSILON)
1128       */
1129      private int getRecursionDepth(CubicSegment curve)
1130      {
1131        double x0 = curve.P1.getX();
1132        double y0 = curve.P1.getY();
1133    
1134        double x1 = curve.cp1.getX();
1135        double y1 = curve.cp1.getY();
1136    
1137        double x2 = curve.cp2.getX();
1138        double y2 = curve.cp2.getY();
1139    
1140        double x3 = curve.P2.getX();
1141        double y3 = curve.P2.getY();
1142    
1143        double L0 = Math.max(Math.max(Math.abs(x0 - 2 * x1 + x2),
1144                                      Math.abs(x1 - 2 * x2 + x3)),
1145                             Math.max(Math.abs(y0 - 2 * y1 + y2),
1146                                      Math.abs(y1 - 2 * y2 + y3)));
1147    
1148        double f = Math.sqrt(2) * 6.0 * L0 / (8.0 * RS_EPSILON);
1149    
1150        int r0 = (int) Math.ceil(Math.log(f) / Math.log(4.0));
1151        return (r0);
1152      }
1153    
1154      /**
1155       * Performs recursive subdivision:
1156       * @param c1 - curve 1
1157       * @param c2 - curve 2
1158       * @param depth1 - recursion depth of curve 1
1159       * @param depth2 - recursion depth of curve 2
1160       * @param t1 - global parametric value of the first curve's starting point
1161       * @param t2 - global parametric value of the second curve's starting point
1162       * @param w1 - global parametric length of curve 1
1163       * @param c1 - global parametric length of curve 2
1164       *
1165       * The final four parameters are for keeping track of the parametric
1166       * value of the curve. For a full curve t = 0, w = 1, w is halved with
1167       * each subdivision.
1168       */
1169      private void recursiveSubdivide(CubicCurve2D c1, CubicCurve2D c2,
1170                                      int depth1, int depth2, double t1,
1171                                      double t2, double w1, double w2)
1172      {
1173        boolean flat1 = depth1 <= 0;
1174        boolean flat2 = depth2 <= 0;
1175    
1176        if (flat1 && flat2)
1177          {
1178            double xlk = c1.getP2().getX() - c1.getP1().getX();
1179            double ylk = c1.getP2().getY() - c1.getP1().getY();
1180    
1181            double xnm = c2.getP2().getX() - c2.getP1().getX();
1182            double ynm = c2.getP2().getY() - c2.getP1().getY();
1183    
1184            double xmk = c2.getP1().getX() - c1.getP1().getX();
1185            double ymk = c2.getP1().getY() - c1.getP1().getY();
1186            double det = xnm * ylk - ynm * xlk;
1187    
1188            if (det + 1.0 == 1.0)
1189              return;
1190    
1191            double detinv = 1.0 / det;
1192            double s = (xnm * ymk - ynm * xmk) * detinv;
1193            double t = (xlk * ymk - ylk * xmk) * detinv;
1194            if ((s < 0.0) || (s > 1.0) || (t < 0.0) || (t > 1.0))
1195              return;
1196    
1197            double[] temp = new double[2];
1198            temp[0] = t1 + s * w1;
1199            temp[1] = t2 + t * w1;
1200            cc_intersections.add(temp);
1201            return;
1202          }
1203    
1204        CubicCurve2D.Double c11 = new CubicCurve2D.Double();
1205        CubicCurve2D.Double c12 = new CubicCurve2D.Double();
1206        CubicCurve2D.Double c21 = new CubicCurve2D.Double();
1207        CubicCurve2D.Double c22 = new CubicCurve2D.Double();
1208    
1209        if (! flat1 && ! flat2)
1210          {
1211            depth1--;
1212            depth2--;
1213            w1 = w1 * 0.5;
1214            w2 = w2 * 0.5;
1215            c1.subdivide(c11, c12);
1216            c2.subdivide(c21, c22);
1217            if (c11.getBounds2D().intersects(c21.getBounds2D()))
1218              recursiveSubdivide(c11, c21, depth1, depth2, t1, t2, w1, w2);
1219            if (c11.getBounds2D().intersects(c22.getBounds2D()))
1220              recursiveSubdivide(c11, c22, depth1, depth2, t1, t2 + w2, w1, w2);
1221            if (c12.getBounds2D().intersects(c21.getBounds2D()))
1222              recursiveSubdivide(c12, c21, depth1, depth2, t1 + w1, t2, w1, w2);
1223            if (c12.getBounds2D().intersects(c22.getBounds2D()))
1224              recursiveSubdivide(c12, c22, depth1, depth2, t1 + w1, t2 + w2, w1, w2);
1225            return;
1226          }
1227    
1228        if (! flat1)
1229          {
1230            depth1--;
1231            c1.subdivide(c11, c12);
1232            w1 = w1 * 0.5;
1233            if (c11.getBounds2D().intersects(c2.getBounds2D()))
1234              recursiveSubdivide(c11, c2, depth1, depth2, t1, t2, w1, w2);
1235            if (c12.getBounds2D().intersects(c2.getBounds2D()))
1236              recursiveSubdivide(c12, c2, depth1, depth2, t1 + w1, t2, w1, w2);
1237            return;
1238          }
1239    
1240        depth2--;
1241        c2.subdivide(c21, c22);
1242        w2 = w2 * 0.5;
1243        if (c1.getBounds2D().intersects(c21.getBounds2D()))
1244          recursiveSubdivide(c1, c21, depth1, depth2, t1, t2, w1, w2);
1245        if (c1.getBounds2D().intersects(c22.getBounds2D()))
1246          recursiveSubdivide(c1, c22, depth1, depth2, t1, t2 + w2, w1, w2);
1247      }
1248    
1249      /**
1250       * Returns a set of interesections between two Cubic segments
1251       * Or null if no intersections were found.
1252       *
1253       * The method used to find the intersection is recursive midpoint
1254       * subdivision. Outline description:
1255       *
1256       * 1) Check if the bounding boxes of the curves intersect,
1257       * 2) If so, divide the curves in the middle and test the bounding
1258       * boxes again,
1259       * 3) Repeat until a maximum recursion depth has been reached, where
1260       * the intersecting curves can be approximated by line segments.
1261       *
1262       * This is a reasonably accurate method, although the recursion depth
1263       * is typically around 20, the bounding-box tests allow for significant
1264       * pruning of the subdivision tree.
1265       */
1266      private Intersection[] cubicCubicIntersect(CubicSegment curve1,
1267                                                 CubicSegment curve2)
1268      {
1269        Rectangle2D r1 = curve1.getBounds();
1270        Rectangle2D r2 = curve2.getBounds();
1271    
1272        if (! r1.intersects(r2))
1273          return null;
1274    
1275        cc_intersections = new Vector();
1276        recursiveSubdivide(curve1.getCubicCurve2D(), curve2.getCubicCurve2D(),
1277                           getRecursionDepth(curve1), getRecursionDepth(curve2),
1278                           0.0, 0.0, 1.0, 1.0);
1279    
1280        if (cc_intersections.size() == 0)
1281          return null;
1282    
1283        Intersection[] results = new Intersection[cc_intersections.size()];
1284        for (int i = 0; i < cc_intersections.size(); i++)
1285          {
1286            double[] temp = (double[]) cc_intersections.elementAt(i);
1287            results[i] = new Intersection(curve1.evaluatePoint(temp[0]), temp[0],
1288                                          temp[1]);
1289          }
1290        cc_intersections = null;
1291        return (results);
1292      }
1293    
1294      /**
1295       * Returns the intersections between a line and a quadratic bezier
1296       * Or null if no intersections are found1
1297       * This is done through combining the line's equation with the
1298       * parametric form of the Bezier and solving the resulting quadratic.
1299       */
1300      private Intersection[] lineQuadIntersect(LineSegment l, QuadSegment c)
1301      {
1302        double[] y = new double[3];
1303        double[] x = new double[3];
1304        double[] r = new double[3];
1305        int nRoots;
1306        double x0 = c.P1.getX();
1307        double y0 = c.P1.getY();
1308        double x1 = c.cp.getX();
1309        double y1 = c.cp.getY();
1310        double x2 = c.P2.getX();
1311        double y2 = c.P2.getY();
1312    
1313        double lx0 = l.P1.getX();
1314        double ly0 = l.P1.getY();
1315        double lx1 = l.P2.getX();
1316        double ly1 = l.P2.getY();
1317        double dx = lx1 - lx0;
1318        double dy = ly1 - ly0;
1319    
1320        // form r(t) = y(t) - x(t) for the bezier
1321        y[0] = y0;
1322        y[1] = 2 * (y1 - y0);
1323        y[2] = (y2 - 2 * y1 + y0);
1324    
1325        x[0] = x0;
1326        x[1] = 2 * (x1 - x0);
1327        x[2] = (x2 - 2 * x1 + x0);
1328    
1329        // a point, not a line
1330        if (dy == 0 && dx == 0)
1331          return null;
1332    
1333        // line on y axis
1334        if (dx == 0 || (dy / dx) > 1.0)
1335          {
1336            double k = dx / dy;
1337            x[0] -= lx0;
1338            y[0] -= ly0;
1339            y[0] *= k;
1340            y[1] *= k;
1341            y[2] *= k;
1342          }
1343        else
1344          {
1345            double k = dy / dx;
1346            x[0] -= lx0;
1347            y[0] -= ly0;
1348            x[0] *= k;
1349            x[1] *= k;
1350            x[2] *= k;
1351          }
1352    
1353        for (int i = 0; i < 3; i++)
1354          r[i] = y[i] - x[i];
1355    
1356        if ((nRoots = QuadCurve2D.solveQuadratic(r)) > 0)
1357          {
1358            Intersection[] temp = new Intersection[nRoots];
1359            int intersections = 0;
1360            for (int i = 0; i < nRoots; i++)
1361              {
1362                double t = r[i];
1363                if (t >= 0.0 && t <= 1.0)
1364                  {
1365                    Point2D p = c.evaluatePoint(t);
1366    
1367                    // if the line is on an axis, snap the point to that axis.
1368                    if (dx == 0)
1369                      p.setLocation(lx0, p.getY());
1370                    if (dy == 0)
1371                      p.setLocation(p.getX(), ly0);
1372    
1373                    if (p.getX() <= Math.max(lx0, lx1)
1374                        && p.getX() >= Math.min(lx0, lx1)
1375                        && p.getY() <= Math.max(ly0, ly1)
1376                        && p.getY() >= Math.min(ly0, ly1))
1377                      {
1378                        double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1);
1379                        temp[i] = new Intersection(p, lineparameter, t);
1380                        intersections++;
1381                      }
1382                  }
1383                else
1384                  temp[i] = null;
1385              }
1386            if (intersections == 0)
1387              return null;
1388    
1389            Intersection[] rValues = new Intersection[intersections];
1390    
1391            for (int i = 0; i < nRoots; i++)
1392              if (temp[i] != null)
1393                rValues[--intersections] = temp[i];
1394            return (rValues);
1395          }
1396        return null;
1397      }
1398    
1399      /**
1400       * Returns the intersections between a line and a cubic segment
1401       * This is done through combining the line's equation with the
1402       * parametric form of the Bezier and solving the resulting quadratic.
1403       */
1404      private Intersection[] lineCubicIntersect(LineSegment l, CubicSegment c)
1405      {
1406        double[] y = new double[4];
1407        double[] x = new double[4];
1408        double[] r = new double[4];
1409        int nRoots;
1410        double x0 = c.P1.getX();
1411        double y0 = c.P1.getY();
1412        double x1 = c.cp1.getX();
1413        double y1 = c.cp1.getY();
1414        double x2 = c.cp2.getX();
1415        double y2 = c.cp2.getY();
1416        double x3 = c.P2.getX();
1417        double y3 = c.P2.getY();
1418    
1419        double lx0 = l.P1.getX();
1420        double ly0 = l.P1.getY();
1421        double lx1 = l.P2.getX();
1422        double ly1 = l.P2.getY();
1423        double dx = lx1 - lx0;
1424        double dy = ly1 - ly0;
1425    
1426        // form r(t) = y(t) - x(t) for the bezier
1427        y[0] = y0;
1428        y[1] = 3 * (y1 - y0);
1429        y[2] = 3 * (y2 + y0 - 2 * y1);
1430        y[3] = y3 - 3 * y2 + 3 * y1 - y0;
1431    
1432        x[0] = x0;
1433        x[1] = 3 * (x1 - x0);
1434        x[2] = 3 * (x2 + x0 - 2 * x1);
1435        x[3] = x3 - 3 * x2 + 3 * x1 - x0;
1436    
1437        // a point, not a line
1438        if (dy == 0 && dx == 0)
1439          return null;
1440    
1441        // line on y axis
1442        if (dx == 0 || (dy / dx) > 1.0)
1443          {
1444            double k = dx / dy;
1445            x[0] -= lx0;
1446            y[0] -= ly0;
1447            y[0] *= k;
1448            y[1] *= k;
1449            y[2] *= k;
1450            y[3] *= k;
1451          }
1452        else
1453          {
1454            double k = dy / dx;
1455            x[0] -= lx0;
1456            y[0] -= ly0;
1457            x[0] *= k;
1458            x[1] *= k;
1459            x[2] *= k;
1460            x[3] *= k;
1461          }
1462        for (int i = 0; i < 4; i++)
1463          r[i] = y[i] - x[i];
1464    
1465        if ((nRoots = CubicCurve2D.solveCubic(r)) > 0)
1466          {
1467            Intersection[] temp = new Intersection[nRoots];
1468            int intersections = 0;
1469            for (int i = 0; i < nRoots; i++)
1470              {
1471                double t = r[i];
1472                if (t >= 0.0 && t <= 1.0)
1473                  {
1474                    // if the line is on an axis, snap the point to that axis.
1475                    Point2D p = c.evaluatePoint(t);
1476                    if (dx == 0)
1477                      p.setLocation(lx0, p.getY());
1478                    if (dy == 0)
1479                      p.setLocation(p.getX(), ly0);
1480    
1481                    if (p.getX() <= Math.max(lx0, lx1)
1482                        && p.getX() >= Math.min(lx0, lx1)
1483                        && p.getY() <= Math.max(ly0, ly1)
1484                        && p.getY() >= Math.min(ly0, ly1))
1485                      {
1486                        double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1);
1487                        temp[i] = new Intersection(p, lineparameter, t);
1488                        intersections++;
1489                      }
1490                  }
1491                else
1492                  temp[i] = null;
1493              }
1494    
1495            if (intersections == 0)
1496              return null;
1497    
1498            Intersection[] rValues = new Intersection[intersections];
1499            for (int i = 0; i < nRoots; i++)
1500              if (temp[i] != null)
1501                rValues[--intersections] = temp[i];
1502            return (rValues);
1503          }
1504        return null;
1505      }
1506    
1507      /**
1508       * Returns the intersection between two lines, or null if there is no
1509       * intersection.
1510       */
1511      private Intersection linesIntersect(LineSegment a, LineSegment b)
1512      {
1513        Point2D P1 = a.P1;
1514        Point2D P2 = a.P2;
1515        Point2D P3 = b.P1;
1516        Point2D P4 = b.P2;
1517    
1518        if (! Line2D.linesIntersect(P1.getX(), P1.getY(), P2.getX(), P2.getY(),
1519                                    P3.getX(), P3.getY(), P4.getX(), P4.getY()))
1520          return null;
1521    
1522        double x1 = P1.getX();
1523        double y1 = P1.getY();
1524        double rx = P2.getX() - x1;
1525        double ry = P2.getY() - y1;
1526    
1527        double x2 = P3.getX();
1528        double y2 = P3.getY();
1529        double sx = P4.getX() - x2;
1530        double sy = P4.getY() - y2;
1531    
1532        double determinant = sx * ry - sy * rx;
1533        double nom = (sx * (y2 - y1) + sy * (x1 - x2));
1534    
1535        // Parallel lines don't intersect. At least we pretend they don't.
1536        if (Math.abs(determinant) < EPSILON)
1537          return null;
1538    
1539        nom = nom / determinant;
1540    
1541        if (nom == 0.0)
1542          return null;
1543        if (nom == 1.0)
1544          return null;
1545    
1546        Point2D p = new Point2D.Double(x1 + nom * rx, y1 + nom * ry);
1547    
1548        return new Intersection(p, p.distance(P1) / P1.distance(P2),
1549                                p.distance(P3) / P3.distance(P4));
1550      }
1551    
1552      /**
1553       * Determines if two points are equal, within an error margin
1554       * 'snap distance'
1555       */
1556      private boolean pointEquals(Point2D a, Point2D b)
1557      {
1558        return (a.equals(b) || a.distance(b) < PE_EPSILON);
1559      }
1560    
1561      /**
1562       * Helper method
1563       * Turns a shape into a Vector of Segments
1564       */
1565      private Vector makeSegment(Shape s)
1566      {
1567        Vector paths = new Vector();
1568        PathIterator pi = s.getPathIterator(null);
1569        double[] coords = new double[6];
1570        Segment subpath = null;
1571        Segment current = null;
1572        double cx;
1573        double cy;
1574        double subpathx;
1575        double subpathy;
1576        cx = cy = subpathx = subpathy = 0.0;
1577    
1578        this.windingRule = pi.getWindingRule();
1579    
1580        while (! pi.isDone())
1581          {
1582            Segment v;
1583            switch (pi.currentSegment(coords))
1584              {
1585              case PathIterator.SEG_MOVETO:
1586                if (subpath != null)
1587                  { // close existing open path
1588                    current.next = new LineSegment(cx, cy, subpathx, subpathy);
1589                    current = current.next;
1590                    current.next = subpath;
1591                  }
1592                subpath = null;
1593                subpathx = cx = coords[0];
1594                subpathy = cy = coords[1];
1595                break;
1596    
1597              // replace 'close' with a line-to.
1598              case PathIterator.SEG_CLOSE:
1599                if (subpath != null && (subpathx != cx || subpathy != cy))
1600                  {
1601                    current.next = new LineSegment(cx, cy, subpathx, subpathy);
1602                    current = current.next;
1603                    current.next = subpath;
1604                    cx = subpathx;
1605                    cy = subpathy;
1606                    subpath = null;
1607                  }
1608                else if (subpath != null)
1609                  {
1610                    current.next = subpath;
1611                    subpath = null;
1612                  }
1613                break;
1614              case PathIterator.SEG_LINETO:
1615                if (cx != coords[0] || cy != coords[1])
1616                  {
1617                    v = new LineSegment(cx, cy, coords[0], coords[1]);
1618                    if (subpath == null)
1619                      {
1620                        subpath = current = v;
1621                        paths.add(subpath);
1622                      }
1623                    else
1624                      {
1625                        current.next = v;
1626                        current = current.next;
1627                      }
1628                    cx = coords[0];
1629                    cy = coords[1];
1630                  }
1631                break;
1632              case PathIterator.SEG_QUADTO:
1633                v = new QuadSegment(cx, cy, coords[0], coords[1], coords[2],
1634                                    coords[3]);
1635                if (subpath == null)
1636                  {
1637                    subpath = current = v;
1638                    paths.add(subpath);
1639                  }
1640                else
1641                  {
1642                    current.next = v;
1643                    current = current.next;
1644                  }
1645                cx = coords[2];
1646                cy = coords[3];
1647                break;
1648              case PathIterator.SEG_CUBICTO:
1649                v = new CubicSegment(cx, cy, coords[0], coords[1], coords[2],
1650                                     coords[3], coords[4], coords[5]);
1651                if (subpath == null)
1652                  {
1653                    subpath = current = v;
1654                    paths.add(subpath);
1655                  }
1656                else
1657                  {
1658                    current.next = v;
1659                    current = current.next;
1660                  }
1661    
1662                // check if the cubic is self-intersecting
1663                double[] lpts = ((CubicSegment) v).getLoop();
1664                if (lpts != null)
1665                  {
1666                    // if it is, break off the loop into its own path.
1667                    v.subdivideInsert(lpts[0]);
1668                    v.next.subdivideInsert((lpts[1] - lpts[0]) / (1.0 - lpts[0]));
1669    
1670                    CubicSegment loop = (CubicSegment) v.next;
1671                    v.next = loop.next;
1672                    loop.next = loop;
1673    
1674                    v.P2 = v.next.P1 = loop.P2 = loop.P1; // snap points
1675                    paths.add(loop);
1676                    current = v.next;
1677                  }
1678    
1679                cx = coords[4];
1680                cy = coords[5];
1681                break;
1682              }
1683            pi.next();
1684          }
1685    
1686        if (subpath != null)
1687          { // close any open path
1688            if (subpathx != cx || subpathy != cy)
1689              {
1690                current.next = new LineSegment(cx, cy, subpathx, subpathy);
1691                current = current.next;
1692                current.next = subpath;
1693              }
1694            else
1695              current.next = subpath;
1696          }
1697    
1698        if (paths.size() == 0)
1699          return (null);
1700    
1701        return (paths);
1702      }
1703    
1704      /**
1705       * Find the intersections of two separate closed paths,
1706       * A and B, split the segments at the intersection points,
1707       * and create nodes pointing from one to the other
1708       */
1709      private int createNodes(Segment A, Segment B)
1710      {
1711        int nNodes = 0;
1712    
1713        Segment a = A;
1714        Segment b = B;
1715    
1716        do
1717          {
1718            do
1719              {
1720                nNodes += a.splitIntersections(b);
1721                b = b.next;
1722              }
1723            while (b != B);
1724    
1725            a = a.next; // move to the next segment
1726          }
1727        while (a != A); // until one wrap.
1728    
1729        return (nNodes);
1730      }
1731    
1732      /**
1733       * Find the intersections of a path with itself.
1734       * Splits the segments at the intersection points,
1735       * and create nodes pointing from one to the other.
1736       */
1737      private int createNodesSelf(Segment A)
1738      {
1739        int nNodes = 0;
1740        Segment a = A;
1741    
1742        if (A.next == A)
1743          return 0;
1744    
1745        do
1746          {
1747            Segment b = a.next;
1748            do
1749              {
1750                if (b != a) // necessary
1751                  nNodes += a.splitIntersections(b);
1752                b = b.next;
1753              }
1754            while (b != A);
1755            a = a.next; // move to the next segment
1756          }
1757        while (a != A); // until one wrap.
1758    
1759        return (nNodes);
1760      }
1761    
1762      /**
1763       * Deletes paths which are redundant from a list, (i.e. solid areas within
1764       * solid areas) Clears any nodes. Sorts the remaining paths into solids
1765       * and holes, sets their orientation and sets the solids and holes lists.
1766       */
1767      private void deleteRedundantPaths(Vector paths)
1768      {
1769        int npaths = paths.size();
1770    
1771        int[][] contains = new int[npaths][npaths];
1772        int[][] windingNumbers = new int[npaths][2];
1773        int neg;
1774        Rectangle2D[] bb = new Rectangle2D[npaths]; // path bounding boxes
1775    
1776        neg = ((windingRule == PathIterator.WIND_NON_ZERO) ? -1 : 1);
1777    
1778        for (int i = 0; i < npaths; i++)
1779          bb[i] = ((Segment) paths.elementAt(i)).getPathBounds();
1780    
1781        // Find which path contains which, assign winding numbers
1782        for (int i = 0; i < npaths; i++)
1783          {
1784            Segment pathA = (Segment) paths.elementAt(i);
1785            pathA.nullNodes(); // remove any now-redundant nodes, in case.
1786            int windingA = pathA.hasClockwiseOrientation() ? 1 : neg;
1787    
1788            for (int j = 0; j < npaths; j++)
1789              if (i != j)
1790                {
1791                  Segment pathB = (Segment) paths.elementAt(j);
1792    
1793                  // A contains B
1794                  if (bb[i].intersects(bb[j]))
1795                    {
1796                      Segment s = pathB.next;
1797                      while (s.P1.getY() == s.P2.getY() && s != pathB)
1798                        s = s.next;
1799                      Point2D p = s.getMidPoint();
1800                      if (pathA.contains(p.getX(), p.getY()))
1801                        contains[i][j] = windingA;
1802                    }
1803                  else
1804                    // A does not contain B
1805                    contains[i][j] = 0;
1806                }
1807              else
1808                contains[i][j] = windingA; // i == j
1809          }
1810    
1811        for (int i = 0; i < npaths; i++)
1812          {
1813            windingNumbers[i][0] = 0;
1814            for (int j = 0; j < npaths; j++)
1815              windingNumbers[i][0] += contains[j][i];
1816            windingNumbers[i][1] = contains[i][i];
1817          }
1818    
1819        Vector solids = new Vector();
1820        Vector holes = new Vector();
1821    
1822        if (windingRule == PathIterator.WIND_NON_ZERO)
1823          {
1824            for (int i = 0; i < npaths; i++)
1825              {
1826                if (windingNumbers[i][0] == 0)
1827                  holes.add(paths.elementAt(i));
1828                else if (windingNumbers[i][0] - windingNumbers[i][1] == 0
1829                         && Math.abs(windingNumbers[i][0]) == 1)
1830                  solids.add(paths.elementAt(i));
1831              }
1832          }
1833        else
1834          {
1835            windingRule = PathIterator.WIND_NON_ZERO;
1836            for (int i = 0; i < npaths; i++)
1837              {
1838                if ((windingNumbers[i][0] & 1) == 0)
1839                  holes.add(paths.elementAt(i));
1840                else if ((windingNumbers[i][0] & 1) == 1)
1841                  solids.add(paths.elementAt(i));
1842              }
1843          }
1844    
1845        setDirection(holes, false);
1846        setDirection(solids, true);
1847        this.holes = holes;
1848        this.solids = solids;
1849      }
1850    
1851      /**
1852       * Sets the winding direction of a Vector of paths
1853       * @param clockwise gives the direction,
1854       * true = clockwise, false = counter-clockwise
1855       */
1856      private void setDirection(Vector paths, boolean clockwise)
1857      {
1858        Segment v;
1859        for (int i = 0; i < paths.size(); i++)
1860          {
1861            v = (Segment) paths.elementAt(i);
1862            if (clockwise != v.hasClockwiseOrientation())
1863              v.reverseAll();
1864          }
1865      }
1866    
1867      /**
1868       * Class representing a linked-list of vertices forming a closed polygon,
1869       * convex or concave, without holes.
1870       */
1871      private abstract class Segment implements Cloneable
1872      {
1873        // segment type, PathIterator segment types are used.
1874        Point2D P1;
1875        Point2D P2;
1876        Segment next;
1877        Segment node;
1878    
1879        Segment()
1880        {
1881          P1 = P2 = null;
1882          node = next = null;
1883        }
1884    
1885        /**
1886         * Reverses the direction of a single segment
1887         */
1888        abstract void reverseCoords();
1889    
1890        /**
1891         * Returns the segment's midpoint
1892         */
1893        abstract Point2D getMidPoint();
1894    
1895        /**
1896         * Returns the bounding box of this segment
1897         */
1898        abstract Rectangle2D getBounds();
1899    
1900        /**
1901         * Transforms a single segment
1902         */
1903        abstract void transform(AffineTransform at);
1904    
1905        /**
1906         * Returns the PathIterator type of a segment
1907         */
1908        abstract int getType();
1909    
1910        /**
1911         */
1912        abstract int splitIntersections(Segment b);
1913    
1914        /**
1915         * Returns the PathIterator coords of a segment
1916         */
1917        abstract int pathIteratorFormat(double[] coords);
1918    
1919        /**
1920         * Returns the number of intersections on the positive X axis,
1921         * with the origin at (x,y), used for contains()-testing
1922         *
1923         * (Although that could be done by the line-intersect methods,
1924         * a dedicated method is better to guarantee consitent handling
1925         * of endpoint-special-cases)
1926         */
1927        abstract int rayCrossing(double x, double y);
1928    
1929        /**
1930         * Subdivides the segment at parametric value t, inserting
1931         * the new segment into the linked list after this,
1932         * such that this becomes [0,t] and this.next becomes [t,1]
1933         */
1934        abstract void subdivideInsert(double t);
1935    
1936        /**
1937         * Returns twice the area of a curve, relative the P1-P2 line
1938         * Used for area calculations.
1939         */
1940        abstract double curveArea();
1941    
1942        /**
1943         * Compare two segments.
1944         */
1945        abstract boolean equals(Segment b);
1946    
1947        /**
1948         * Determines if this path of segments contains the point (x,y)
1949         */
1950        boolean contains(double x, double y)
1951        {
1952          Segment v = this;
1953          int crossings = 0;
1954          do
1955            {
1956              int n = v.rayCrossing(x, y);
1957              crossings += n;
1958              v = v.next;
1959            }
1960          while (v != this);
1961          return ((crossings & 1) == 1);
1962        }
1963    
1964        /**
1965         * Nulls all nodes of the path. Clean up any 'hairs'.
1966         */
1967        void nullNodes()
1968        {
1969          Segment v = this;
1970          do
1971            {
1972              v.node = null;
1973              v = v.next;
1974            }
1975          while (v != this);
1976        }
1977    
1978        /**
1979         * Transforms each segment in the closed path
1980         */
1981        void transformSegmentList(AffineTransform at)
1982        {
1983          Segment v = this;
1984          do
1985            {
1986              v.transform(at);
1987              v = v.next;
1988            }
1989          while (v != this);
1990        }
1991    
1992        /**
1993         * Determines the winding direction of the path
1994         * By the sign of the area.
1995         */
1996        boolean hasClockwiseOrientation()
1997        {
1998          return (getSignedArea() > 0.0);
1999        }
2000    
2001        /**
2002         * Returns the bounds of this path
2003         */
2004        public Rectangle2D getPathBounds()
2005        {
2006          double xmin;
2007          double xmax;
2008          double ymin;
2009          double ymax;
2010          xmin = xmax = P1.getX();
2011          ymin = ymax = P1.getY();
2012    
2013          Segment v = this;
2014          do
2015            {
2016              Rectangle2D r = v.getBounds();
2017              xmin = Math.min(r.getMinX(), xmin);
2018              ymin = Math.min(r.getMinY(), ymin);
2019              xmax = Math.max(r.getMaxX(), xmax);
2020              ymax = Math.max(r.getMaxY(), ymax);
2021              v = v.next;
2022            }
2023          while (v != this);
2024    
2025          return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
2026        }
2027    
2028        /**
2029         * Calculates twice the signed area of the path;
2030         */
2031        double getSignedArea()
2032        {
2033          Segment s;
2034          double area = 0.0;
2035    
2036          s = this;
2037          do
2038            {
2039              area += s.curveArea();
2040    
2041              area += s.P1.getX() * s.next.P1.getY()
2042              - s.P1.getY() * s.next.P1.getX();
2043              s = s.next;
2044            }
2045          while (s != this);
2046    
2047          return area;
2048        }
2049    
2050        /**
2051         * Reverses the orientation of the whole polygon
2052         */
2053        void reverseAll()
2054        {
2055          reverseCoords();
2056          Segment v = next;
2057          Segment former = this;
2058          while (v != this)
2059            {
2060              v.reverseCoords();
2061              Segment vnext = v.next;
2062              v.next = former;
2063              former = v;
2064              v = vnext;
2065            }
2066          next = former;
2067        }
2068    
2069        /**
2070         * Inserts a Segment after this one
2071         */
2072        void insert(Segment v)
2073        {
2074          Segment n = next;
2075          next = v;
2076          v.next = n;
2077        }
2078    
2079        /**
2080         * Returns if this segment path is polygonal
2081         */
2082        boolean isPolygonal()
2083        {
2084          Segment v = this;
2085          do
2086            {
2087              if (! (v instanceof LineSegment))
2088                return false;
2089              v = v.next;
2090            }
2091          while (v != this);
2092          return true;
2093        }
2094    
2095        /**
2096         * Clones this path
2097         */
2098        Segment cloneSegmentList() throws CloneNotSupportedException
2099        {
2100          Vector list = new Vector();
2101          Segment v = next;
2102    
2103          while (v != this)
2104            {
2105              list.add(v);
2106              v = v.next;
2107            }
2108    
2109          Segment clone = (Segment) this.clone();
2110          v = clone;
2111          for (int i = 0; i < list.size(); i++)
2112            {
2113              clone.next = (Segment) ((Segment) list.elementAt(i)).clone();
2114              clone = clone.next;
2115            }
2116          clone.next = v;
2117          return v;
2118        }
2119    
2120        /**
2121         * Creates a node between this segment and segment b
2122         * at the given intersection
2123         * @return the number of nodes created (0 or 1)
2124         */
2125        int createNode(Segment b, Intersection i)
2126        {
2127          Point2D p = i.p;
2128          if ((pointEquals(P1, p) || pointEquals(P2, p))
2129              && (pointEquals(b.P1, p) || pointEquals(b.P2, p)))
2130            return 0;
2131    
2132          subdivideInsert(i.ta);
2133          b.subdivideInsert(i.tb);
2134    
2135          // snap points
2136          b.P2 = b.next.P1 = P2 = next.P1 = i.p;
2137    
2138          node = b.next;
2139          b.node = next;
2140          return 1;
2141        }
2142    
2143        /**
2144         * Creates multiple nodes from a list of intersections,
2145         * This must be done in the order of ascending parameters,
2146         * and the parameters must be recalculated in accordance
2147         * with each split.
2148         * @return the number of nodes created
2149         */
2150        protected int createNodes(Segment b, Intersection[] x)
2151        {
2152          Vector v = new Vector();
2153          for (int i = 0; i < x.length; i++)
2154            {
2155              Point2D p = x[i].p;
2156              if (! ((pointEquals(P1, p) || pointEquals(P2, p))
2157                  && (pointEquals(b.P1, p) || pointEquals(b.P2, p))))
2158                v.add(x[i]);
2159            }
2160    
2161          int nNodes = v.size();
2162          Intersection[] A = new Intersection[nNodes];
2163          Intersection[] B = new Intersection[nNodes];
2164          for (int i = 0; i < nNodes; i++)
2165            A[i] = B[i] = (Intersection) v.elementAt(i);
2166    
2167          // Create two lists sorted by the parameter
2168          // Bubble sort, OK I suppose, since the number of intersections
2169          // cannot be larger than 9 (cubic-cubic worst case) anyway
2170          for (int i = 0; i < nNodes - 1; i++)
2171            {
2172              for (int j = i + 1; j < nNodes; j++)
2173                {
2174                  if (A[i].ta > A[j].ta)
2175                    {
2176                      Intersection swap = A[i];
2177                      A[i] = A[j];
2178                      A[j] = swap;
2179                    }
2180                  if (B[i].tb > B[j].tb)
2181                    {
2182                      Intersection swap = B[i];
2183                      B[i] = B[j];
2184                      B[j] = swap;
2185                    }
2186                }
2187            }
2188          // subdivide a
2189          Segment s = this;
2190          for (int i = 0; i < nNodes; i++)
2191            {
2192              s.subdivideInsert(A[i].ta);
2193    
2194              // renormalize the parameters
2195              for (int j = i + 1; j < nNodes; j++)
2196                A[j].ta = (A[j].ta - A[i].ta) / (1.0 - A[i].ta);
2197    
2198              A[i].seg = s;
2199              s = s.next;
2200            }
2201    
2202          // subdivide b, set nodes
2203          s = b;
2204          for (int i = 0; i < nNodes; i++)
2205            {
2206              s.subdivideInsert(B[i].tb);
2207    
2208              for (int j = i + 1; j < nNodes; j++)
2209                B[j].tb = (B[j].tb - B[i].tb) / (1.0 - B[i].tb);
2210    
2211              // set nodes
2212              B[i].seg.node = s.next; // node a -> b
2213              s.node = B[i].seg.next; // node b -> a
2214    
2215              // snap points
2216              B[i].seg.P2 = B[i].seg.next.P1 = s.P2 = s.next.P1 = B[i].p;
2217              s = s.next;
2218            }
2219          return nNodes;
2220        }
2221    
2222        /**
2223         * Determines if two paths are equal.
2224         * Colinear line segments are ignored in the comparison.
2225         */
2226        boolean pathEquals(Segment B)
2227        {
2228          if (! getPathBounds().equals(B.getPathBounds()))
2229            return false;
2230    
2231          Segment startA = getTopLeft();
2232          Segment startB = B.getTopLeft();
2233          Segment a = startA;
2234          Segment b = startB;
2235          do
2236            {
2237              if (! a.equals(b))
2238                return false;
2239    
2240              if (a instanceof LineSegment)
2241                a = ((LineSegment) a).lastCoLinear();
2242              if (b instanceof LineSegment)
2243                b = ((LineSegment) b).lastCoLinear();
2244    
2245              a = a.next;
2246              b = b.next;
2247            }
2248          while (a != startA && b != startB);
2249          return true;
2250        }
2251    
2252        /**
2253         * Return the segment with the top-leftmost first point
2254         */
2255        Segment getTopLeft()
2256        {
2257          Segment v = this;
2258          Segment tl = this;
2259          do
2260            {
2261              if (v.P1.getY() < tl.P1.getY())
2262                tl = v;
2263              else if (v.P1.getY() == tl.P1.getY())
2264                {
2265                  if (v.P1.getX() < tl.P1.getX())
2266                    tl = v;
2267                }
2268              v = v.next;
2269            }
2270          while (v != this);
2271          return tl;
2272        }
2273    
2274        /**
2275         * Returns if the path has a segment outside a shape
2276         */
2277        boolean isSegmentOutside(Shape shape)
2278        {
2279          return ! shape.contains(getMidPoint());
2280        }
2281      } // class Segment
2282    
2283      private class LineSegment extends Segment
2284      {
2285        public LineSegment(double x1, double y1, double x2, double y2)
2286        {
2287          super();
2288          P1 = new Point2D.Double(x1, y1);
2289          P2 = new Point2D.Double(x2, y2);
2290        }
2291    
2292        public LineSegment(Point2D p1, Point2D p2)
2293        {
2294          super();
2295          P1 = (Point2D) p1.clone();
2296          P2 = (Point2D) p2.clone();
2297        }
2298    
2299        /**
2300         * Clones this segment
2301         */
2302        public Object clone()
2303        {
2304          return new LineSegment(P1, P2);
2305        }
2306    
2307        /**
2308         * Transforms the segment
2309         */
2310        void transform(AffineTransform at)
2311        {
2312          P1 = at.transform(P1, null);
2313          P2 = at.transform(P2, null);
2314        }
2315    
2316        /**
2317         * Swap start and end points
2318         */
2319        void reverseCoords()
2320        {
2321          Point2D p = P1;
2322          P1 = P2;
2323          P2 = p;
2324        }
2325    
2326        /**
2327         * Returns the segment's midpoint
2328         */
2329        Point2D getMidPoint()
2330        {
2331          return (new Point2D.Double(0.5 * (P1.getX() + P2.getX()),
2332                                     0.5 * (P1.getY() + P2.getY())));
2333        }
2334    
2335        /**
2336         * Returns twice the area of a curve, relative the P1-P2 line
2337         * Obviously, a line does not enclose any area besides the line
2338         */
2339        double curveArea()
2340        {
2341          return 0;
2342        }
2343    
2344        /**
2345         * Returns the PathIterator type of a segment
2346         */
2347        int getType()
2348        {
2349          return (PathIterator.SEG_LINETO);
2350        }
2351    
2352        /**
2353         * Subdivides the segment at parametric value t, inserting
2354         * the new segment into the linked list after this,
2355         * such that this becomes [0,t] and this.next becomes [t,1]
2356         */
2357        void subdivideInsert(double t)
2358        {
2359          Point2D p = new Point2D.Double((P2.getX() - P1.getX()) * t + P1.getX(),
2360                                         (P2.getY() - P1.getY()) * t + P1.getY());
2361          insert(new LineSegment(p, P2));
2362          P2 = p;
2363          next.node = node;
2364          node = null;
2365        }
2366    
2367        /**
2368         * Determines if two line segments are strictly colinear
2369         */
2370        boolean isCoLinear(LineSegment b)
2371        {
2372          double x1 = P1.getX();
2373          double y1 = P1.getY();
2374          double x2 = P2.getX();
2375          double y2 = P2.getY();
2376          double x3 = b.P1.getX();
2377          double y3 = b.P1.getY();
2378          double x4 = b.P2.getX();
2379          double y4 = b.P2.getY();
2380    
2381          if ((y1 - y3) * (x4 - x3) - (x1 - x3) * (y4 - y3) != 0.0)
2382            return false;
2383    
2384          return ((x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3) == 0.0);
2385        }
2386    
2387        /**
2388         * Return the last segment colinear with this one.
2389         * Used in comparing paths.
2390         */
2391        Segment lastCoLinear()
2392        {
2393          Segment prev = this;
2394          Segment v = next;
2395    
2396          while (v instanceof LineSegment)
2397            {
2398              if (isCoLinear((LineSegment) v))
2399                {
2400                  prev = v;
2401                  v = v.next;
2402                }
2403              else
2404                return prev;
2405            }
2406          return prev;
2407        }
2408    
2409        /**
2410         * Compare two segments.
2411         * We must take into account that the lines may be broken into colinear
2412         * subsegments and ignore them.
2413         */
2414        boolean equals(Segment b)
2415        {
2416          if (! (b instanceof LineSegment))
2417            return false;
2418          Point2D p1 = P1;
2419          Point2D p3 = b.P1;
2420    
2421          if (! p1.equals(p3))
2422            return false;
2423    
2424          Point2D p2 = lastCoLinear().P2;
2425          Point2D p4 = ((LineSegment) b).lastCoLinear().P2;
2426          return (p2.equals(p4));
2427        }
2428    
2429        /**
2430         * Returns a line segment
2431         */
2432        int pathIteratorFormat(double[] coords)
2433        {
2434          coords[0] = P2.getX();
2435          coords[1] = P2.getY();
2436          return (PathIterator.SEG_LINETO);
2437        }
2438    
2439        /**
2440         * Returns if the line has intersections.
2441         */
2442        boolean hasIntersections(Segment b)
2443        {
2444          if (b instanceof LineSegment)
2445            return (linesIntersect(this, (LineSegment) b) != null);
2446    
2447          if (b instanceof QuadSegment)
2448            return (lineQuadIntersect(this, (QuadSegment) b) != null);
2449    
2450          if (b instanceof CubicSegment)
2451            return (lineCubicIntersect(this, (CubicSegment) b) != null);
2452    
2453          return false;
2454        }
2455    
2456        /**
2457         * Splits intersections into nodes,
2458         * This one handles line-line, line-quadratic, line-cubic
2459         */
2460        int splitIntersections(Segment b)
2461        {
2462          if (b instanceof LineSegment)
2463            {
2464              Intersection i = linesIntersect(this, (LineSegment) b);
2465    
2466              if (i == null)
2467                return 0;
2468    
2469              return createNode(b, i);
2470            }
2471    
2472          Intersection[] x = null;
2473    
2474          if (b instanceof QuadSegment)
2475            x = lineQuadIntersect(this, (QuadSegment) b);
2476    
2477          if (b instanceof CubicSegment)
2478            x = lineCubicIntersect(this, (CubicSegment) b);
2479    
2480          if (x == null)
2481            return 0;
2482    
2483          if (x.length == 1)
2484            return createNode(b, (Intersection) x[0]);
2485    
2486          return createNodes(b, x);
2487        }
2488    
2489        /**
2490         * Returns the bounding box of this segment
2491         */
2492        Rectangle2D getBounds()
2493        {
2494          return (new Rectangle2D.Double(Math.min(P1.getX(), P2.getX()),
2495                                         Math.min(P1.getY(), P2.getY()),
2496                                         Math.abs(P1.getX() - P2.getX()),
2497                                         Math.abs(P1.getY() - P2.getY())));
2498        }
2499    
2500        /**
2501         * Returns the number of intersections on the positive X axis,
2502         * with the origin at (x,y), used for contains()-testing
2503         */
2504        int rayCrossing(double x, double y)
2505        {
2506          double x0 = P1.getX() - x;
2507          double y0 = P1.getY() - y;
2508          double x1 = P2.getX() - x;
2509          double y1 = P2.getY() - y;
2510    
2511          if (y0 * y1 > 0)
2512            return 0;
2513    
2514          if (x0 < 0 && x1 < 0)
2515            return 0;
2516    
2517          if (y0 == 0.0)
2518            y0 += EPSILON;
2519    
2520          if (y1 == 0.0)
2521            y1 += EPSILON;
2522    
2523          if (Line2D.linesIntersect(x0, y0, x1, y1, 0.0, 0.0, Double.MAX_VALUE, 0.0))
2524            return 1;
2525          return 0;
2526        }
2527      } // class LineSegment
2528    
2529      /**
2530       * Quadratic Bezier curve segment
2531       *
2532       * Note: Most peers don't support quadratics directly, so it might make
2533       * sense to represent them as cubics internally and just be done with it.
2534       * I think we should be peer-agnostic, however, and stay faithful to the
2535       * input geometry types as far as possible.
2536       */
2537      private class QuadSegment extends Segment
2538      {
2539        Point2D cp; // control point
2540    
2541        /**
2542         * Constructor, takes the coordinates of the start, control,
2543         * and end point, respectively.
2544         */
2545        QuadSegment(double x1, double y1, double cx, double cy, double x2,
2546                    double y2)
2547        {
2548          super();
2549          P1 = new Point2D.Double(x1, y1);
2550          P2 = new Point2D.Double(x2, y2);
2551          cp = new Point2D.Double(cx, cy);
2552        }
2553    
2554        /**
2555         * Clones this segment
2556         */
2557        public Object clone()
2558        {
2559          return new QuadSegment(P1.getX(), P1.getY(), cp.getX(), cp.getY(),
2560                                 P2.getX(), P2.getY());
2561        }
2562    
2563        /**
2564         * Returns twice the area of a curve, relative the P1-P2 line
2565         *
2566         * The area formula can be derived by using Green's formula in the
2567         * plane on the parametric form of the bezier.
2568         */
2569        double curveArea()
2570        {
2571          double x0 = P1.getX();
2572          double y0 = P1.getY();
2573          double x1 = cp.getX();
2574          double y1 = cp.getY();
2575          double x2 = P2.getX();
2576          double y2 = P2.getY();
2577    
2578          double P = (y2 - 2 * y1 + y0);
2579          double Q = 2 * (y1 - y0);
2580          double R = y0;
2581    
2582          double A = (x2 - 2 * x1 + x0);
2583          double B = 2 * (x1 - x0);
2584          double C = x0;
2585    
2586          double area = (B * P - A * Q) / 3.0;
2587          return (area);
2588        }
2589    
2590        /**
2591         * Compare two segments.
2592         */
2593        boolean equals(Segment b)
2594        {
2595          if (! (b instanceof QuadSegment))
2596            return false;
2597    
2598          return (P1.equals(b.P1) && cp.equals(((QuadSegment) b).cp)
2599                 && P2.equals(b.P2));
2600        }
2601    
2602        /**
2603         * Returns a Point2D corresponding to the parametric value t
2604         * of the curve
2605         */
2606        Point2D evaluatePoint(double t)
2607        {
2608          double x0 = P1.getX();
2609          double y0 = P1.getY();
2610          double x1 = cp.getX();
2611          double y1 = cp.getY();
2612          double x2 = P2.getX();
2613          double y2 = P2.getY();
2614    
2615          return new Point2D.Double(t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0)
2616                                    + x0,
2617                                    t * t * (y2 - 2 * y1 + y0) + 2 * t * (y1 - y0)
2618                                    + y0);
2619        }
2620    
2621        /**
2622         * Returns the bounding box of this segment
2623         */
2624        Rectangle2D getBounds()
2625        {
2626          double x0 = P1.getX();
2627          double y0 = P1.getY();
2628          double x1 = cp.getX();
2629          double y1 = cp.getY();
2630          double x2 = P2.getX();
2631          double y2 = P2.getY();
2632          double r0;
2633          double r1;
2634    
2635          double xmax = Math.max(x0, x2);
2636          double ymax = Math.max(y0, y2);
2637          double xmin = Math.min(x0, x2);
2638          double ymin = Math.min(y0, y2);
2639    
2640          r0 = 2 * (y1 - y0);
2641          r1 = 2 * (y2 - 2 * y1 + y0);
2642          if (r1 != 0.0)
2643            {
2644              double t = -r0 / r1;
2645              if (t > 0.0 && t < 1.0)
2646                {
2647                  double y = evaluatePoint(t).getY();
2648                  ymax = Math.max(y, ymax);
2649                  ymin = Math.min(y, ymin);
2650                }
2651            }
2652          r0 = 2 * (x1 - x0);
2653          r1 = 2 * (x2 - 2 * x1 + x0);
2654          if (r1 != 0.0)
2655            {
2656              double t = -r0 / r1;
2657              if (t > 0.0 && t < 1.0)
2658                {
2659                  double x = evaluatePoint(t).getY();
2660                  xmax = Math.max(x, xmax);
2661                  xmin = Math.min(x, xmin);
2662                }
2663            }
2664    
2665          return (new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin));
2666        }
2667    
2668        /**
2669         * Returns a cubic segment corresponding to this curve
2670         */
2671        CubicSegment getCubicSegment()
2672        {
2673          double x1 = P1.getX() + 2.0 * (cp.getX() - P1.getX()) / 3.0;
2674          double y1 = P1.getY() + 2.0 * (cp.getY() - P1.getY()) / 3.0;
2675          double x2 = cp.getX() + (P2.getX() - cp.getX()) / 3.0;
2676          double y2 = cp.getY() + (P2.getY() - cp.getY()) / 3.0;
2677    
2678          return new CubicSegment(P1.getX(), P1.getY(), x1, y1, x2, y2, P2.getX(),
2679                                  P2.getY());
2680        }
2681    
2682        /**
2683         * Returns the segment's midpoint
2684         */
2685        Point2D getMidPoint()
2686        {
2687          return evaluatePoint(0.5);
2688        }
2689    
2690        /**
2691         * Returns the PathIterator type of a segment
2692         */
2693        int getType()
2694        {
2695          return (PathIterator.SEG_QUADTO);
2696        }
2697    
2698        /**
2699         * Returns the PathIterator coords of a segment
2700         */
2701        int pathIteratorFormat(double[] coords)
2702        {
2703          coords[0] = cp.getX();
2704          coords[1] = cp.getY();
2705          coords[2] = P2.getX();
2706          coords[3] = P2.getY();
2707          return (PathIterator.SEG_QUADTO);
2708        }
2709    
2710        /**
2711         * Returns the number of intersections on the positive X axis,
2712         * with the origin at (x,y), used for contains()-testing
2713         */
2714        int rayCrossing(double x, double y)
2715        {
2716          double x0 = P1.getX() - x;
2717          double y0 = P1.getY() - y;
2718          double x1 = cp.getX() - x;
2719          double y1 = cp.getY() - y;
2720          double x2 = P2.getX() - x;
2721          double y2 = P2.getY() - y;
2722          double[] r = new double[3];
2723          int nRoots;
2724          int nCrossings = 0;
2725    
2726          /* check if curve may intersect X+ axis. */
2727          if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) && (y0 * y1 <= 0 || y1 * y2 <= 0))
2728            {
2729              if (y0 == 0.0)
2730                y0 += EPSILON;
2731              if (y2 == 0.0)
2732                y2 += EPSILON;
2733    
2734              r[0] = y0;
2735              r[1] = 2 * (y1 - y0);
2736              r[2] = (y2 - 2 * y1 + y0);
2737    
2738              nRoots = QuadCurve2D.solveQuadratic(r);
2739              for (int i = 0; i < nRoots; i++)
2740                if (r[i] > 0.0f && r[i] < 1.0f)
2741                  {
2742                    double t = r[i];
2743                    if (t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + x0 > 0.0)
2744                      nCrossings++;
2745                  }
2746            }
2747          return nCrossings;
2748        }
2749    
2750        /**
2751         * Swap start and end points
2752         */
2753        void reverseCoords()
2754        {
2755          Point2D temp = P1;
2756          P1 = P2;
2757          P2 = temp;
2758        }
2759    
2760        /**
2761         * Splits intersections into nodes,
2762         * This one handles quadratic-quadratic only,
2763         * Quadratic-line is passed on to the LineSegment class,
2764         * Quadratic-cubic is passed on to the CubicSegment class
2765         */
2766        int splitIntersections(Segment b)
2767        {
2768          if (b instanceof LineSegment)
2769            return (b.splitIntersections(this));
2770    
2771          if (b instanceof CubicSegment)
2772            return (b.splitIntersections(this));
2773    
2774          if (b instanceof QuadSegment)
2775            {
2776              // Use the cubic-cubic intersection routine for quads as well,
2777              // Since a quadratic can be exactly described as a cubic, this
2778              // should not be a problem;
2779              // The recursion depth will be the same in any case.
2780              Intersection[] x = cubicCubicIntersect(getCubicSegment(),
2781                                                     ((QuadSegment) b)
2782                                                     .getCubicSegment());
2783              if (x == null)
2784                return 0;
2785    
2786              if (x.length == 1)
2787                return createNode(b, (Intersection) x[0]);
2788    
2789              return createNodes(b, x);
2790            }
2791          return 0;
2792        }
2793    
2794        /**
2795         * Subdivides the segment at parametric value t, inserting
2796         * the new segment into the linked list after this,
2797         * such that this becomes [0,t] and this.next becomes [t,1]
2798         */
2799        void subdivideInsert(double t)
2800        {
2801          double x0 = P1.getX();
2802          double y0 = P1.getY();
2803          double x1 = cp.getX();
2804          double y1 = cp.getY();
2805          double x2 = P2.getX();
2806          double y2 = P2.getY();
2807    
2808          double p10x = x0 + t * (x1 - x0);
2809          double p10y = y0 + t * (y1 - y0);
2810          double p11x = x1 + t * (x2 - x1);
2811          double p11y = y1 + t * (y2 - y1);
2812          double p20x = p10x + t * (p11x - p10x);
2813          double p20y = p10y + t * (p11y - p10y);
2814    
2815          insert(new QuadSegment(p20x, p20y, p11x, p11y, x2, y2));
2816          P2 = next.P1;
2817          cp.setLocation(p10x, p10y);
2818    
2819          next.node = node;
2820          node = null;
2821        }
2822    
2823        /**
2824         * Transforms the segment
2825         */
2826        void transform(AffineTransform at)
2827        {
2828          P1 = at.transform(P1, null);
2829          P2 = at.transform(P2, null);
2830          cp = at.transform(cp, null);
2831        }
2832      } // class QuadSegment
2833    
2834      /**
2835       * Cubic Bezier curve segment
2836       */
2837      private class CubicSegment extends Segment
2838      {
2839        Point2D cp1; // control points
2840        Point2D cp2; // control points
2841    
2842        /**
2843         * Constructor - takes coordinates of the starting point,
2844         * first control point, second control point and end point,
2845         * respecively.
2846         */
2847        public CubicSegment(double x1, double y1, double c1x, double c1y,
2848                            double c2x, double c2y, double x2, double y2)
2849        {
2850          super();
2851          P1 = new Point2D.Double(x1, y1);
2852          P2 = new Point2D.Double(x2, y2);
2853          cp1 = new Point2D.Double(c1x, c1y);
2854          cp2 = new Point2D.Double(c2x, c2y);
2855        }
2856    
2857        /**
2858         * Clones this segment
2859         */
2860        public Object clone()
2861        {
2862          return new CubicSegment(P1.getX(), P1.getY(), cp1.getX(), cp1.getY(),
2863                                  cp2.getX(), cp2.getY(), P2.getX(), P2.getY());
2864        }
2865    
2866        /**
2867         * Returns twice the area of a curve, relative the P1-P2 line
2868         *
2869         * The area formula can be derived by using Green's formula in the
2870         * plane on the parametric form of the bezier.
2871         */
2872        double curveArea()
2873        {
2874          double x0 = P1.getX();
2875          double y0 = P1.getY();
2876          double x1 = cp1.getX();
2877          double y1 = cp1.getY();
2878          double x2 = cp2.getX();
2879          double y2 = cp2.getY();
2880          double x3 = P2.getX();
2881          double y3 = P2.getY();
2882    
2883          double P = y3 - 3 * y2 + 3 * y1 - y0;
2884          double Q = 3 * (y2 + y0 - 2 * y1);
2885          double R = 3 * (y1 - y0);
2886          double S = y0;
2887    
2888          double A = x3 - 3 * x2 + 3 * x1 - x0;
2889          double B = 3 * (x2 + x0 - 2 * x1);
2890          double C = 3 * (x1 - x0);
2891          double D = x0;
2892    
2893          double area = (B * P - A * Q) / 5.0 + (C * P - A * R) / 2.0
2894                        + (C * Q - B * R) / 3.0;
2895    
2896          return (area);
2897        }
2898    
2899        /**
2900         * Compare two segments.
2901         */
2902        boolean equals(Segment b)
2903        {
2904          if (! (b instanceof CubicSegment))
2905            return false;
2906    
2907          return (P1.equals(b.P1) && cp1.equals(((CubicSegment) b).cp1)
2908                 && cp2.equals(((CubicSegment) b).cp2) && P2.equals(b.P2));
2909        }
2910    
2911        /**
2912         * Returns a Point2D corresponding to the parametric value t
2913         * of the curve
2914         */
2915        Point2D evaluatePoint(double t)
2916        {
2917          double x0 = P1.getX();
2918          double y0 = P1.getY();
2919          double x1 = cp1.getX();
2920          double y1 = cp1.getY();
2921          double x2 = cp2.getX();
2922          double y2 = cp2.getY();
2923          double x3 = P2.getX();
2924          double y3 = P2.getY();
2925    
2926          return new Point2D.Double(-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3)
2927                                    + 3 * t * t * (x0 - 2 * x1 + x2)
2928                                    + 3 * t * (x1 - x0) + x0,
2929                                    -(t * t * t) * (y0 - 3 * y1 + 3 * y2 - y3)
2930                                    + 3 * t * t * (y0 - 2 * y1 + y2)
2931                                    + 3 * t * (y1 - y0) + y0);
2932        }
2933    
2934        /**
2935         * Returns the bounding box of this segment
2936         */
2937        Rectangle2D getBounds()
2938        {
2939          double x0 = P1.getX();
2940          double y0 = P1.getY();
2941          double x1 = cp1.getX();
2942          double y1 = cp1.getY();
2943          double x2 = cp2.getX();
2944          double y2 = cp2.getY();
2945          double x3 = P2.getX();
2946          double y3 = P2.getY();
2947          double[] r = new double[3];
2948    
2949          double xmax = Math.max(x0, x3);
2950          double ymax = Math.max(y0, y3);
2951          double xmin = Math.min(x0, x3);
2952          double ymin = Math.min(y0, y3);
2953    
2954          r[0] = 3 * (y1 - y0);
2955          r[1] = 6.0 * (y2 + y0 - 2 * y1);
2956          r[2] = 3.0 * (y3 - 3 * y2 + 3 * y1 - y0);
2957    
2958          int n = QuadCurve2D.solveQuadratic(r);
2959          for (int i = 0; i < n; i++)
2960            {
2961              double t = r[i];
2962              if (t > 0 && t < 1.0)
2963                {
2964                  double y = evaluatePoint(t).getY();
2965                  ymax = Math.max(y, ymax);
2966                  ymin = Math.min(y, ymin);
2967                }
2968            }
2969    
2970          r[0] = 3 * (x1 - x0);
2971          r[1] = 6.0 * (x2 + x0 - 2 * x1);
2972          r[2] = 3.0 * (x3 - 3 * x2 + 3 * x1 - x0);
2973          n = QuadCurve2D.solveQuadratic(r);
2974          for (int i = 0; i < n; i++)
2975            {
2976              double t = r[i];
2977              if (t > 0 && t < 1.0)
2978                {
2979                  double x = evaluatePoint(t).getX();
2980                  xmax = Math.max(x, xmax);
2981                  xmin = Math.min(x, xmin);
2982                }
2983            }
2984          return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
2985        }
2986    
2987        /**
2988         * Returns a CubicCurve2D object corresponding to this segment.
2989         */
2990        CubicCurve2D getCubicCurve2D()
2991        {
2992          return new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(),
2993                                         cp1.getY(), cp2.getX(), cp2.getY(),
2994                                         P2.getX(), P2.getY());
2995        }
2996    
2997        /**
2998         * Returns the parametric points of self-intersection if the cubic
2999         * is self-intersecting, null otherwise.
3000         */
3001        double[] getLoop()
3002        {
3003          double x0 = P1.getX();
3004          double y0 = P1.getY();
3005          double x1 = cp1.getX();
3006          double y1 = cp1.getY();
3007          double x2 = cp2.getX();
3008          double y2 = cp2.getY();
3009          double x3 = P2.getX();
3010          double y3 = P2.getY();
3011          double[] r = new double[4];
3012          double k;
3013          double R;
3014          double T;
3015          double A;
3016          double B;
3017          double[] results = new double[2];
3018    
3019          R = x3 - 3 * x2 + 3 * x1 - x0;
3020          T = y3 - 3 * y2 + 3 * y1 - y0;
3021    
3022          // A qudratic
3023          if (R == 0.0 && T == 0.0)
3024            return null;
3025    
3026          // true cubic
3027          if (R != 0.0 && T != 0.0)
3028            {
3029              A = 3 * (x2 + x0 - 2 * x1) / R;
3030              B = 3 * (x1 - x0) / R;
3031    
3032              double P = 3 * (y2 + y0 - 2 * y1) / T;
3033              double Q = 3 * (y1 - y0) / T;
3034    
3035              if (A == P || Q == B)
3036                return null;
3037    
3038              k = (Q - B) / (A - P);
3039            }
3040          else
3041            {
3042              if (R == 0.0)
3043                {
3044                  // quadratic in x
3045                  k = -(3 * (x1 - x0)) / (3 * (x2 + x0 - 2 * x1));
3046                  A = 3 * (y2 + y0 - 2 * y1) / T;
3047                  B = 3 * (y1 - y0) / T;
3048                }
3049              else
3050                {
3051                  // quadratic in y
3052                  k = -(3 * (y1 - y0)) / (3 * (y2 + y0 - 2 * y1));
3053                  A = 3 * (x2 + x0 - 2 * x1) / R;
3054                  B = 3 * (x1 - x0) / R;
3055                }
3056            }
3057    
3058          r[0] = -k * k * k - A * k * k - B * k;
3059          r[1] = 3 * k * k + 2 * k * A + 2 * B;
3060          r[2] = -3 * k;
3061          r[3] = 2;
3062    
3063          int n = CubicCurve2D.solveCubic(r);
3064          if (n != 3)
3065            return null;
3066    
3067          // sort r
3068          double t;
3069          for (int i = 0; i < 2; i++)
3070            for (int j = i + 1; j < 3; j++)
3071              if (r[j] < r[i])
3072                {
3073                  t = r[i];
3074                  r[i] = r[j];
3075                  r[j] = t;
3076                }
3077    
3078          if (Math.abs(r[0] + r[2] - k) < 1E-13)
3079            if (r[0] >= 0.0 && r[0] <= 1.0 && r[2] >= 0.0 && r[2] <= 1.0)
3080              if (evaluatePoint(r[0]).distance(evaluatePoint(r[2])) < PE_EPSILON * 10)
3081                { // we snap the points anyway
3082                  results[0] = r[0];
3083                  results[1] = r[2];
3084                  return (results);
3085                }
3086          return null;
3087        }
3088    
3089        /**
3090         * Returns the segment's midpoint
3091         */
3092        Point2D getMidPoint()
3093        {
3094          return evaluatePoint(0.5);
3095        }
3096    
3097        /**
3098         * Returns the PathIterator type of a segment
3099         */
3100        int getType()
3101        {
3102          return (PathIterator.SEG_CUBICTO);
3103        }
3104    
3105        /**
3106         * Returns the PathIterator coords of a segment
3107         */
3108        int pathIteratorFormat(double[] coords)
3109        {
3110          coords[0] = cp1.getX();
3111          coords[1] = cp1.getY();
3112          coords[2] = cp2.getX();
3113          coords[3] = cp2.getY();
3114          coords[4] = P2.getX();
3115          coords[5] = P2.getY();
3116          return (PathIterator.SEG_CUBICTO);
3117        }
3118    
3119        /**
3120         * Returns the number of intersections on the positive X axis,
3121         * with the origin at (x,y), used for contains()-testing
3122         */
3123        int rayCrossing(double x, double y)
3124        {
3125          double x0 = P1.getX() - x;
3126          double y0 = P1.getY() - y;
3127          double x1 = cp1.getX() - x;
3128          double y1 = cp1.getY() - y;
3129          double x2 = cp2.getX() - x;
3130          double y2 = cp2.getY() - y;
3131          double x3 = P2.getX() - x;
3132          double y3 = P2.getY() - y;
3133          double[] r = new double[4];
3134          int nRoots;
3135          int nCrossings = 0;
3136    
3137          /* check if curve may intersect X+ axis. */
3138          if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
3139              && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
3140            {
3141              if (y0 == 0.0)
3142                y0 += EPSILON;
3143              if (y3 == 0.0)
3144                y3 += EPSILON;
3145    
3146              r[0] = y0;
3147              r[1] = 3 * (y1 - y0);
3148              r[2] = 3 * (y2 + y0 - 2 * y1);
3149              r[3] = y3 - 3 * y2 + 3 * y1 - y0;
3150    
3151              if ((nRoots = CubicCurve2D.solveCubic(r)) > 0)
3152                for (int i = 0; i < nRoots; i++)
3153                  {
3154                    if (r[i] > 0.0 && r[i] < 1.0)
3155                      {
3156                        double t = r[i];
3157                        if (-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3)
3158                            + 3 * t * t * (x0 - 2 * x1 + x2) + 3 * t * (x1 - x0)
3159                            + x0 > 0.0)
3160                          nCrossings++;
3161                      }
3162                  }
3163            }
3164          return nCrossings;
3165        }
3166    
3167        /**
3168         * Swap start and end points
3169         */
3170        void reverseCoords()
3171        {
3172          Point2D p = P1;
3173          P1 = P2;
3174          P2 = p;
3175          p = cp1; // swap control points
3176          cp1 = cp2;
3177          cp2 = p;
3178        }
3179    
3180        /**
3181         * Splits intersections into nodes,
3182         * This one handles cubic-cubic and cubic-quadratic intersections
3183         */
3184        int splitIntersections(Segment b)
3185        {
3186          if (b instanceof LineSegment)
3187            return (b.splitIntersections(this));
3188    
3189          Intersection[] x = null;
3190    
3191          if (b instanceof QuadSegment)
3192            x = cubicCubicIntersect(this, ((QuadSegment) b).getCubicSegment());
3193    
3194          if (b instanceof CubicSegment)
3195            x = cubicCubicIntersect(this, (CubicSegment) b);
3196    
3197          if (x == null)
3198            return 0;
3199    
3200          if (x.length == 1)
3201            return createNode(b, x[0]);
3202    
3203          return createNodes(b, x);
3204        }
3205    
3206        /**
3207         * Subdivides the segment at parametric value t, inserting
3208         * the new segment into the linked list after this,
3209         * such that this becomes [0,t] and this.next becomes [t,1]
3210         */
3211        void subdivideInsert(double t)
3212        {
3213          CubicSegment s = (CubicSegment) clone();
3214          double p1x = (s.cp1.getX() - s.P1.getX()) * t + s.P1.getX();
3215          double p1y = (s.cp1.getY() - s.P1.getY()) * t + s.P1.getY();
3216    
3217          double px = (s.cp2.getX() - s.cp1.getX()) * t + s.cp1.getX();
3218          double py = (s.cp2.getY() - s.cp1.getY()) * t + s.cp1.getY();
3219    
3220          s.cp2.setLocation((s.P2.getX() - s.cp2.getX()) * t + s.cp2.getX(),
3221                            (s.P2.getY() - s.cp2.getY()) * t + s.cp2.getY());
3222    
3223          s.cp1.setLocation((s.cp2.getX() - px) * t + px,
3224                            (s.cp2.getY() - py) * t + py);
3225    
3226          double p2x = (px - p1x) * t + p1x;
3227          double p2y = (py - p1y) * t + p1y;
3228    
3229          double p3x = (s.cp1.getX() - p2x) * t + p2x;
3230          double p3y = (s.cp1.getY() - p2y) * t + p2y;
3231          s.P1.setLocation(p3x, p3y);
3232    
3233          // insert new curve
3234          insert(s);
3235    
3236          // set this curve
3237          cp1.setLocation(p1x, p1y);
3238          cp2.setLocation(p2x, p2y);
3239          P2 = s.P1;
3240          next.node = node;
3241          node = null;
3242        }
3243    
3244        /**
3245         * Transforms the segment
3246         */
3247        void transform(AffineTransform at)
3248        {
3249          P1 = at.transform(P1, null);
3250          P2 = at.transform(P2, null);
3251          cp1 = at.transform(cp1, null);
3252          cp2 = at.transform(cp2, null);
3253        }
3254      } // class CubicSegment
3255  } // class Area  } // class Area

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.1.2.1

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26