View Javadoc

1   // Copyright 2001-2004 FreeHEP
2   package org.freehep.graphicsio;
3   
4   import java.awt.geom.Point2D;
5   import java.io.IOException;
6   
7   /**
8    * Implements the Cubic Bezier Curve PathConstructor functionality in terms of
9    * Quadratic Bezier Curves
10   * 
11   * Uses the same algorithm published as ActionScript (SWF) by Robert Penner:
12   * 
13   * ========================== Cubic Bezier Drawing v1.1
14   * ========================== recursive quadratic approximation with adjustable
15   * tolerance
16   * 
17   * March 4, 2004
18   * 
19   * Robert Penner www.robertpenner.com/tools/bezier_cubic.zip file:
20   * bezier_draw_cubic.as ==========================
21   * 
22   * @author Mark Donszelmann
23   * @version $Id: CubicToQuadPathConstructor.java 9319 2006-11-13 22:08:58Z duns $
24   */
25  public abstract class CubicToQuadPathConstructor extends
26          AbstractPathConstructor {
27  
28      private double resolutionSq;
29  
30      protected CubicToQuadPathConstructor(double resolution) {
31          super();
32          resolutionSq = resolution * resolution;
33      }
34  
35      public void move(double x, double y) throws IOException {
36          currentX = x;
37          currentY = y;
38      }
39  
40      public void line(double x, double y) throws IOException {
41          currentX = x;
42          currentY = y;
43      }
44  
45      public void cubic(double x1, double y1, double x2, double y2, double x3,
46              double y3) throws IOException {
47          quadratify(new Point2D.Double(currentX, currentY), new Point2D.Double(
48                  x1, y1), new Point2D.Double(x2, y2), new Point2D.Double(x3, y3));
49  
50          currentX = x3;
51          currentY = y3;
52      }
53  
54      public void closePath(double x0, double y0) throws IOException {
55          currentX = 0;
56          currentY = 0;
57      }
58  
59      public static Point2D intersect(Point2D p1, Point2D p2, Point2D p3,
60              Point2D p4) {
61  
62          double dx1 = p2.getX() - p1.getX();
63          double dx2 = p3.getX() - p4.getX();
64          
65          // line are vertical
66          if ((dx1 == 0) && (dx2 == 0)) return null;
67  
68          double dy1 = p2.getY() - p1.getY();
69          double dy2 = p3.getY() - p4.getY();
70          
71          // line are horizontal
72          if ((dy1 == 0) && (dy2 == 0)) return null;
73          
74          double m1 = (p2.getY() - p1.getY()) / dx1;
75          double m2 = (p3.getY() - p4.getY()) / dx2;
76  
77          if (dx1 == 0) {
78              // infinity
79              return new Point2D.Double(p1.getX(), m2 * (p1.getX() - p4.getX())
80                      + p4.getY());
81          } else if (dx2 == 0) {
82              // infinity
83              return new Point2D.Double(p4.getX(), m1 * (p4.getX() - p1.getX())
84                      + p1.getY());
85          }
86          
87          // lines are parallel
88          if (m1 == m2) return null;
89          
90          double x = (-m2 * p4.getX() + p4.getY() + m1 * p1.getX() - p1.getY())
91                  / (m1 - m2);
92          double y = m1 * (x - p1.getX()) + p1.getY();
93          return new Point2D.Double(x, y);
94      }
95  
96      public static Point2D midPoint(Point2D a, Point2D b) {
97          return new Point2D.Double((a.getX() + b.getX()) / 2.0, (a.getY() + b
98                  .getY()) / 2.0);
99      }
100 
101     public void quadratify(Point2D a, Point2D b, Point2D c, Point2D d)
102             throws IOException {
103         // find intersection between bezier arms
104         Point2D s = intersect(a, b, c, d);
105         if (s == null) return;
106         
107         // find distance between the midpoints
108         double dx = (a.getX() + d.getX() + s.getX() * 4 - (b.getX() + c.getX()) * 3) * .125;
109         double dy = (a.getY() + d.getY() + s.getY() * 4 - (b.getY() + c.getY()) * 3) * .125;
110         // split curve if the quadratic isn't close enough
111         if (dx * dx + dy * dy > resolutionSq) {
112             Point2D p01 = midPoint(a, b);
113             Point2D p12 = midPoint(b, c);
114             Point2D p23 = midPoint(c, d);
115             Point2D p02 = midPoint(p01, p12);
116             Point2D p13 = midPoint(p12, p23);
117             Point2D p03 = midPoint(p02, p13);
118             // recursive call to subdivide curve
119             quadratify(a, p01, p02, p03);
120             quadratify(p03, p13, p23, d);
121         } else {
122             // end recursion by drawing quadratic bezier
123             quad(s.getX(), s.getY(), d.getX(), d.getY());
124         }
125     }
126 
127     static class Test extends CubicToQuadPathConstructor {
128         public Test(double resolution) {
129             super(resolution);
130         }
131 
132         public void quad(double x1, double y1, double x2, double y2) {
133             System.out.println("Quad: (" + currentX + ", " + currentY + ") ("
134                     + x1 + ", " + y1 + ") (" + x2 + ", " + y2 + ")");
135             currentX = x2;
136             currentY = y2;
137         }
138 
139     }
140 
141     public static void main(String[] args) throws Exception {
142         PathConstructor pc = new Test(0.5);
143         // A, B, C, D
144         pc.move(20, 20);
145         pc.cubic(20, 40, 40, 60, 60, 60);
146 
147         // A, B, D, C
148         pc.move(20, 20);
149         pc.cubic(20, 40, 60, 60, 40, 60);
150 
151         // Intersecting Curve
152         pc.move(183, 149);
153         pc.cubic(189, 291, 256, 347, 295, 244);
154         pc.cubic(334, 141, 286, 216, 214, 228);
155         pc.cubic(142, 240, 142, 256, 176, 284);
156     }
157 }