Hi,

To round off the stroking work, the attached patch implements
BasicStroke.dashedStroke().  It also follows a flattening approach, but
can be modified relatively easily to take a subdividing-curves approach
(no point at the moment, since CubicSegment.getDisplacedSegments will
flatten it anyways).

As always, comments are appreciated.

Thanks,
Francis


2007-07-28  Francis Kung  <[EMAIL PROTECTED]>

        * java/awt/BasicStroke.java
        (dashedStroke): Implemented.

Index: java/awt/BasicStroke.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/BasicStroke.java,v
retrieving revision 1.15
diff -u -r1.15 BasicStroke.java
--- java/awt/BasicStroke.java	27 Jul 2006 20:59:41 -0000	1.15
+++ java/awt/BasicStroke.java	28 Jul 2006 20:54:40 -0000
@@ -43,6 +43,7 @@
 import gnu.java.awt.java2d.QuadSegment;
 import gnu.java.awt.java2d.Segment;
 
+import java.awt.geom.FlatteningPathIterator;
 import java.awt.geom.GeneralPath;
 import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
@@ -486,8 +487,157 @@
 
   private Shape dashedStroke(PathIterator pi)
   {
-    GeneralPath out = new GeneralPath();
-    return out;
+    // The choice of (flatnessSq == width / 3) is made to be consistent with
+    // the flattening in CubicSegment.getDisplacedSegments
+    FlatteningPathIterator flat = new FlatteningPathIterator(pi,
+                                                             Math.sqrt(width / 3));
+
+    // Holds the endpoint of the current segment (or piece of a segment)
+    double[] coords = new double[2];
+
+    // Holds end of the last segment
+    double x, y, x0, y0;
+    x = x0 = y = y0 = 0;
+
+    // Various useful flags
+    boolean pathOpen = false;
+    boolean dashOn = true;
+    boolean offsetting = (phase != 0);
+
+    // How far we are into the current dash
+    double distance = 0;
+    int dashIndex = 0;
+
+    // And variables to hold the final output
+    GeneralPath output = new GeneralPath();
+    Segment[] p;
+
+    // Iterate over the FlatteningPathIterator
+    while (! flat.isDone())
+      {
+        switch (flat.currentSegment(coords))
+          {
+          case PathIterator.SEG_MOVETO:
+            x0 = x = coords[0];
+            y0 = y = coords[1];
+
+            if (pathOpen)
+              {
+                capEnds();
+                convertPath(output, start);
+                start = end = null;
+                pathOpen = false;
+              }
+
+            break;
+
+          case PathIterator.SEG_LINETO:
+            boolean segmentConsumed = false;
+
+            while (! segmentConsumed)
+              {
+                // Find the total remaining length of this segment
+                double segLength = Math.sqrt((x - coords[0]) * (x - coords[0])
+                                             + (y - coords[1])
+                                             * (y - coords[1]));
+                boolean spanBoundary = true;
+                double[] segmentEnd = null;
+
+                // The current segment fits entirely inside the current dash
+                if ((offsetting && distance + segLength <= phase)
+                    || distance + segLength <= dash[dashIndex])
+                  {
+                    spanBoundary = false;
+                  }
+                
+                // Otherwise, we need to split the segment in two, as this
+                // segment spans a dash boundry
+                else
+                  {
+                    segmentEnd = coords.clone();
+
+                    // Calculate the remaining distance in this dash,
+                    // and coordinates of the dash boundary
+                    double reqLength;
+                    if (offsetting)
+                      reqLength = phase - distance;
+                    else
+                      reqLength = dash[dashIndex] - distance;
+
+                    coords[0] = x + ((coords[0] - x) * reqLength / segLength);
+                    coords[1] = y + ((coords[1] - y) * reqLength / segLength);
+                  }
+
+                if (offsetting || ! dashOn)
+                  {
+                    // Dash is off, or we are in offset - treat this as a
+                    // moveTo
+                    x0 = x = coords[0];
+                    y0 = y = coords[1];
+
+                    if (pathOpen)
+                      {
+                        capEnds();
+                        convertPath(output, start);
+                        start = end = null;
+                        pathOpen = false;
+                      }
+                  }
+                else
+                  {
+                    // Dash is on - treat this as a lineTo
+                    p = (new LineSegment(x, y, coords[0], coords[1])).getDisplacedSegments(width / 2.0);
+
+                    if (! pathOpen)
+                      {
+                        start = p[0];
+                        end = p[1];
+                        pathOpen = true;
+                      }
+                    else
+                      addSegments(p);
+
+                    x = coords[0];
+                    y = coords[1];
+                  }
+
+                // Update variables depending on whether we spanned a
+                // dash boundary or not
+                if (! spanBoundary)
+                  {
+                    distance += segLength;
+                    segmentConsumed = true;
+                  }
+                else
+                  {
+                    if (offsetting)
+                      offsetting = false;
+                    dashOn = ! dashOn;
+                    distance = 0;
+                    coords = segmentEnd;
+
+                    if (dashIndex + 1 == dash.length)
+                      dashIndex = 0;
+                    else
+                      dashIndex++;
+
+                    // Since the value of segmentConsumed is still false,
+                    // the next run of the while loop will complete the segment
+                  }
+              }
+            break;
+
+          // This is a flattened path, so we don't need to deal with curves
+          }
+        flat.next();
+      }
+
+    if (pathOpen)
+      {
+        capEnds();
+        convertPath(output, start);
+      }
+    return output;
   }
 
   /**

Reply via email to