Author: [EMAIL PROTECTED]
Date: Tue Nov  4 16:53:44 2008
New Revision: 405

Added:
     
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java
    
(contents, props changed)
Removed:
     
piccolo2d.java/trunk/extras/src/test/java/edu/umd/cs/piccolox/util/PFixedWidthStrokeMRO.java
     
piccolo2d.java/trunk/extras/src/test/java/edu/umd/cs/piccolox/util/PFixedWidthStrokeTest.java
Modified:
     
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java

Log:
issue#49 issue#48 done for the first. Is it still compatible?

Modified:  
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java
==============================================================================
---  
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java
        
(original)
+++  
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java
        
Tue Nov  4 16:53:44 2008
@@ -31,381 +31,120 @@
  import java.awt.BasicStroke;
  import java.awt.Shape;
  import java.awt.Stroke;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
+import java.io.ObjectStreamException;
  import java.io.Serializable;

-import sun.dc.path.FastPathProducer;
-import sun.dc.path.PathConsumer;
-import sun.dc.path.PathException;
-import sun.dc.pr.PathDasher;
-import sun.dc.pr.PathStroker;
-import sun.dc.pr.Rasterizer;
-
-import edu.umd.cs.piccolo.util.PAffineTransform;
-import edu.umd.cs.piccolo.util.PDebug;
-import edu.umd.cs.piccolo.util.PPaintContext;
-import edu.umd.cs.piccolo.util.PPickPath;
-
  /**
- * <b>PFixedWidthStroke</b> is the same as [EMAIL PROTECTED] 
java.awt.BasicStroke}  
except
- * that PFixedWidthStroke has a fixed width on the screen so that even  
when the
- * canvas view is zooming its width stays the same in canvas coordinates.  
Note
- * that this stroke draws in the inside of the stroked shape, instead of  
the
- * normal draw on center behavior.
- * <P>
+ * <b>PFixedWidthStroke</b> is the same as [EMAIL PROTECTED] BasicStroke} 
except that
+ * PFixedWidthStroke has a fixed width on the screen so that even when the
+ * canvas view is zooming its width stays the same in canvas coordinates.
+ * <p>
+ * [EMAIL PROTECTED] #createStrokedShape(Shape)} checks if the scale has 
changed  
since the
+ * last usage and if that's the case calls [EMAIL PROTECTED] 
#newStroke(float)} to  
get a
+ * new [EMAIL PROTECTED] Stroke} instance to delegate to.
+ * <p>
+ * <b>CAUTION!</b> this implementation falls short for large scaling  
factors -
+ * the effective miterlimit might drop below 1.0 which isn't permitted by
+ * [EMAIL PROTECTED] BasicStroke} and therefore limited to a minimal 1.0 by 
this
+ * implementation. A more sophisticated implementation might use the  
approach
+ * mentioned at http://code.google.com/p/piccolo2d/issues/detail?id=49
+ * <p>
+ * <b>CAUTION!</b> after extreme scaling this implementation seems to  
change to
+ * internal state of the base stroke. Try PathExample with extreme zoom in  
and
+ * zoom back to the original scale. The pickable circles disappear.  
Strange!
   *
   * @see edu.umd.cs.piccolo.nodes.PPath
+ * @see BasicStroke
   * @version 1.0
   * @author Jesse Grosjean
+ * @author Marcus Rohrmoser
   */
-public class PFixedWidthStroke implements Stroke, Serializable {
-
-    private static PAffineTransform TEMP_TRANSFORM = new  
PAffineTransform();
-    private static GeneralPath TEMP_PATH = new  
GeneralPath(GeneralPath.WIND_NON_ZERO);
-
-    final static int JOIN_MITER = BasicStroke.JOIN_MITER;
-    final static int JOIN_ROUND = BasicStroke.JOIN_ROUND;
-    final static int JOIN_BEVEL = BasicStroke.JOIN_BEVEL;
-    final static int CAP_BUTT = BasicStroke.CAP_BUTT;
-    final static int CAP_ROUND = BasicStroke.CAP_ROUND;
-    final static int CAP_SQUARE = BasicStroke.CAP_SQUARE;
-
-    private float width;
-    private int join;
-    private int cap;
-    private float miterlimit;
-    private float dash[];
-    private float dash_phase;
-
-    private static final int RasterizerCaps[] = { Rasterizer.BUTT,  
Rasterizer.ROUND, Rasterizer.SQUARE };
-
-    private static final int RasterizerCorners[] = { Rasterizer.MITER,  
Rasterizer.ROUND, Rasterizer.BEVEL };
-
-    private class FillAdapter implements PathConsumer {
-        boolean closed;
-        GeneralPath path;
-
-        public FillAdapter() {
-            path = TEMP_PATH;
-            path.reset();
-        }
-
-        public Shape getShape() {
-            return path;
-        }
-
-        public void beginPath() {
-        }
-
-        public void beginSubpath(float x0, float y0) {
-            if (closed) {
-                path.closePath();
-                closed = false;
-            }
-            path.moveTo(x0, y0);
-        }
-
-        public void appendLine(float x1, float y1) {
-            path.lineTo(x1, y1);
-        }
-
-        public void appendQuadratic(float xm, float ym, float x1, float  
y1) {
-            path.quadTo(xm, ym, x1, y1);
-        }
-
-        public void appendCubic(float xm, float ym, float xn, float yn,  
float x1, float y1) {
-            path.curveTo(xm, ym, xn, yn, x1, y1);
-        }
-
-        public void closedSubpath() {
-            closed = true;
-        }
-
-        public void endPath() {
-            if (closed) {
-                path.closePath();
-                closed = false;
-            }
-        }
+public class PFixedWidthStroke extends PSemanticStroke implements  
Serializable {

-        public void useProxy(FastPathProducer proxy) throws PathException {
-            proxy.sendTo(this);
-        }
-
-        public long getCPathConsumer() {
-            return 0;
-        }
+    // make them public if required or delete when cleaning up for 2.0
+    private static final int CAP_BUTT = BasicStroke.CAP_BUTT;
+    private static final int CAP_ROUND = BasicStroke.CAP_ROUND;
+    private static final int CAP_SQUARE = BasicStroke.CAP_SQUARE;
+    private static final int JOIN_BEVEL = BasicStroke.JOIN_BEVEL;
+    private static final int JOIN_MITER = BasicStroke.JOIN_MITER;
+    private static final int JOIN_ROUND = BasicStroke.JOIN_ROUND;
+
+    private static final long serialVersionUID = -2503357070350473610L;
+
+    // avoid repeated cloning:
+    private transient final float dash[];
+    // avoid repeated instantiations:
+    private transient final float tmpDash[];

-        public void dispose() {
-        }
-
-        public PathConsumer getConsumer() {
-            return null;
-        }
+    public PFixedWidthStroke() {
+        this(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f,  
null, 0.0f);
      }

-    public PFixedWidthStroke() {
-        this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
+    /** This should be "public" and the "main" constructor. */
+    private PFixedWidthStroke(final BasicStroke stroke) {
+        super(stroke);
+        dash = stroke.getDashArray();
+        tmpDash = dash == null ? null : new float[dash.length];
      }

-    public PFixedWidthStroke(float width) {
-        this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
+    public PFixedWidthStroke(final float width) {
+        this(width, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f,  
null, 0.0f);
      }

-    public PFixedWidthStroke(float width, int cap, int join) {
+    public PFixedWidthStroke(final float width, final int cap, final int  
join) {
          this(width, cap, join, 10.0f, null, 0.0f);
      }

-    public PFixedWidthStroke(float width, int cap, int join, float  
miterlimit) {
+    public PFixedWidthStroke(final float width, final int cap, final int  
join, final float miterlimit) {
          this(width, cap, join, miterlimit, null, 0.0f);
      }

-    public PFixedWidthStroke(float width, int cap, int join, float  
miterlimit, float dash[], float dash_phase) {
-        if (width < 0.0f) {
-            throw new IllegalArgumentException("negative width");
-        }
-        if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
-            throw new IllegalArgumentException("illegal end cap value");
-        }
-        if (join == JOIN_MITER) {
-            if (miterlimit < 1.0f) {
-                throw new IllegalArgumentException("miter limit < 1");
-            }
-        }
-        else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
-            throw new IllegalArgumentException("illegal line join value");
-        }
-        if (dash != null) {
-            if (dash_phase < 0.0f) {
-                throw new IllegalArgumentException("negative dash phase");
-            }
-            boolean allzero = true;
-            for (int i = 0; i < dash.length; i++) {
-                float d = dash[i];
-                if (d > 0.0) {
-                    allzero = false;
-                }
-                else if (d < 0.0) {
-                    throw new IllegalArgumentException("negative dash  
length");
-                }
-            }
-
-            if (allzero) {
-                throw new IllegalArgumentException("dash lengths all  
zero");
-            }
-        }
-        this.width = width;
-        this.cap = cap;
-        this.join = join;
-        this.miterlimit = miterlimit;
-        if (dash != null) {
-            this.dash = (float[]) dash.clone();
-        }
-        this.dash_phase = dash_phase;
+    public PFixedWidthStroke(final float width, final int cap, final int  
join, final float miterlimit,
+            final float dash[], final float dash_phase) {
+        this(new BasicStroke(width, cap, join, miterlimit, dash,  
dash_phase));
      }

      public Object clone() {
-        try {
-            return super.clone();
-        }
-        catch (CloneNotSupportedException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    public Shape createStrokedShape(Shape s) {
-        FillAdapter filler = new FillAdapter();
-        PathStroker stroker = new PathStroker(filler);
-        PathConsumer consumer;
-
-        // Fixed Width Additions, always stroke path inside shape.
-        // Also keeps dashes fixed when zooming (thanks to Shawn  
Castrianni -
-        // Dec 2006)
-        float fixedScale = 1.0f;
-
-        if (PDebug.getProcessingOutput()) {
-            if (PPaintContext.CURRENT_PAINT_CONTEXT != null) {
-                fixedScale = 1.0f / (float)  
PPaintContext.CURRENT_PAINT_CONTEXT.getScale();
-            }
-        }
-        else {
-            if (PPickPath.CURRENT_PICK_PATH != null) {
-                fixedScale = 1.0f / (float)  
PPickPath.CURRENT_PICK_PATH.getScale();
-            }
-        }
-        float fixedWidth = width * fixedScale;
-
-        Rectangle2D bounds = s.getBounds2D();
-        double scale = 1.0;
-
-        if (bounds.getWidth() > bounds.getHeight()) {
-            if (bounds.getWidth() != 0) {
-                scale = (bounds.getWidth() - fixedWidth) /  
bounds.getWidth();
-            }
-        }
-        else {
-            if (bounds.getHeight() != 0) {
-                scale = (bounds.getHeight() - fixedWidth) /  
bounds.getHeight();
-            }
-        }
-
-        TEMP_TRANSFORM.setToIdentity();
-        TEMP_TRANSFORM.scaleAboutPoint(scale, bounds.getCenterX(),  
bounds.getCenterY());
-        stroker.setPenDiameter(fixedWidth);
-        PathIterator pi = s.getPathIterator(TEMP_TRANSFORM);
-
-        stroker.setPenT4(null);
-        stroker.setCaps(RasterizerCaps[cap]);
-        stroker.setCorners(RasterizerCorners[join], miterlimit);
-        if (dash != null) {
-            // Fixed Width Additions
-            float fixedDash[] = new float[dash.length];
-            for (int i = 0; i < dash.length; i++) {
-                fixedDash[i] = dash[i] * fixedScale;
-            }
-            float fixedDashPhase = dash_phase * fixedScale;
-            PathDasher dasher = new PathDasher(stroker);
-            dasher.setDash(fixedDash, fixedDashPhase);
-            dasher.setDashT4(null);
-            consumer = dasher;
-        }
-        else {
-            consumer = stroker;
-        }
-
-        try {
-            consumer.beginPath();
-            boolean pathClosed = false;
-            float mx = 0.0f;
-            float my = 0.0f;
-            float point[] = new float[6];
-
-            while (!pi.isDone()) {
-                int type = pi.currentSegment(point);
-                if (pathClosed == true) {
-                    pathClosed = false;
-                    if (type != PathIterator.SEG_MOVETO) {
-                        // Force current point back to last moveto point
-                        consumer.beginSubpath(mx, my);
-                    }
-                }
-                switch (type) {
-                    case PathIterator.SEG_MOVETO:
-                        mx = point[0];
-                        my = point[1];
-                        consumer.beginSubpath(point[0], point[1]);
-                        break;
-                    case PathIterator.SEG_LINETO:
-                        consumer.appendLine(point[0], point[1]);
-                        break;
-                    case PathIterator.SEG_QUADTO:
-                        // Quadratic curves take two points
-                        consumer.appendQuadratic(point[0], point[1],  
point[2], point[3]);
-                        break;
-                    case PathIterator.SEG_CUBICTO:
-                        // Cubic curves take three points
-                        consumer.appendCubic(point[0], point[1], point[2],  
point[3], point[4], point[5]);
-                        break;
-                    case PathIterator.SEG_CLOSE:
-                        consumer.closedSubpath();
-                        pathClosed = true;
-                        break;
-                }
-                pi.next();
-            }
-
-            consumer.endPath();
-
-            consumer.dispose(); // hack to fix memory leak, shouldn't be
-                                // neccessary but is.
-        }
-        catch (PathException e) {
-            throw new InternalError("Unable to Stroke shape (" +  
e.getMessage() + ")");
-        }
-
-        return filler.getShape();
+        throw new UnsupportedOperationException("Not implemented.");
      }

-    public boolean equals(Object obj) {
-        if (!(obj instanceof PFixedWidthStroke)) {
-            return false;
-        }
-
-        PFixedWidthStroke bs = (PFixedWidthStroke) obj;
-        if (width != bs.width) {
-            return false;
-        }
-
-        if (join != bs.join) {
-            return false;
-        }
-
-        if (cap != bs.cap) {
-            return false;
-        }
-
-        if (miterlimit != bs.miterlimit) {
-            return false;
-        }
-
-        if (dash != null) {
-            if (dash_phase != bs.dash_phase) {
-                return false;
-            }
-
-            if (!java.util.Arrays.equals(dash, bs.dash)) {
-                return false;
-            }
-        }
-        else if (bs.dash != null) {
-            return false;
-        }
-
-        return true;
+    public float[] getDashArray() {
+        return ((BasicStroke) stroke).getDashArray();
      }

-    private float[] getDashArray() {
-        if (dash == null) {
-            return null;
-        }
-
-        return (float[]) dash.clone();
-    }
-
-    private float getDashPhase() {
-        return dash_phase;
+    public float getDashPhase() {
+        return ((BasicStroke) stroke).getDashPhase();
      }

-    private int getEndCap() {
-        return cap;
+    public int getEndCap() {
+        return ((BasicStroke) stroke).getEndCap();
      }

-    private int getLineJoin() {
-        return join;
+    public int getLineJoin() {
+        return ((BasicStroke) stroke).getLineJoin();
      }

-    private float getLineWidth() {
-        return width;
+    public float getLineWidth() {
+        return ((BasicStroke) stroke).getLineWidth();
      }

-    private float getMiterLimit() {
-        return miterlimit;
+    public float getMiterLimit() {
+        return ((BasicStroke) stroke).getMiterLimit();
      }

-    public int hashCode() {
-        int hash = Float.floatToIntBits(width);
-        hash = hash * 31 + join;
-        hash = hash * 31 + cap;
-        hash = hash * 31 + Float.floatToIntBits(miterlimit);
-        if (dash != null) {
-            hash = hash * 31 + Float.floatToIntBits(dash_phase);
-            for (int i = 0; i < dash.length; i++) {
-                hash = hash * 31 + Float.floatToIntBits(dash[i]);
+    protected Stroke newStroke(final float activeScale) {
+        if (tmpDash != null) {
+            for (int i = dash.length - 1; i >= 0; i--) {
+                tmpDash[i] = dash[i] / activeScale;
              }
          }
-        return hash;
+        final float ml = getMiterLimit() / activeScale;
+        return new BasicStroke(getLineWidth() / activeScale, getEndCap(),  
getLineJoin(), ml < 1.0f ? 1.0f : ml,
+                tmpDash, getDashPhase() / activeScale);
+    }
+
+    /** Is it really necessary to implement [EMAIL PROTECTED] Serializable}? */
+    protected Object readResolve() throws ObjectStreamException {
+        return new PFixedWidthStroke((BasicStroke) stroke);
      }
-}
\ No newline at end of file
+}

Added:  
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java
==============================================================================
--- (empty file)
+++  
piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java
  
Tue Nov  4 16:53:44 2008
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2008, Piccolo2D project, http://piccolo2d.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without  
modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,  
this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright  
notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other  
materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the  
Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from  
this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS  
IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF  
MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  
DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  
DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  
CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  
USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.umd.cs.piccolox.util;
+
+import java.awt.Shape;
+import java.awt.Stroke;
+
+import edu.umd.cs.piccolo.util.PDebug;
+import edu.umd.cs.piccolo.util.PPaintContext;
+import edu.umd.cs.piccolo.util.PPickPath;
+
+/**
+ *
+ * @see edu.umd.cs.piccolo.nodes.PPath
+ * @see Stroke
+ * @version 1.3
+ * @author Marcus Rohrmoser
+ */
+abstract class PSemanticStroke implements Stroke {
+    protected static final double THRESHOLD = 1e-6;
+
+    private transient float recentScale;
+    private transient Stroke recentStroke;
+    protected final Stroke stroke;
+
+    protected PSemanticStroke(final Stroke stroke) {
+        this.stroke = recentStroke = stroke;
+        recentScale = 1.0F;
+    }
+
+    /**
+     * Ask [EMAIL PROTECTED] #getActiveScale()}, call [EMAIL PROTECTED] 
#newStroke(float)} if
+     * necessary and delegate to [EMAIL PROTECTED] 
Stroke#createStrokedShape(Shape)}
+     */
+    public Shape createStrokedShape(final Shape s) {
+        final float currentScale = getActiveScale();
+        if (Math.abs(currentScale - recentScale) > THRESHOLD) {
+            recentStroke = newStroke(recentScale = currentScale);
+        }
+        return recentStroke.createStrokedShape(s);
+    }
+
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PSemanticStroke other = (PSemanticStroke) obj;
+        if (stroke == null) {
+            if (other.stroke != null) {
+                return false;
+            }
+        }
+        else if (!stroke.equals(other.stroke)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Detect the current scale. Made protected to enable custom
+     * re-implementations.
+     */
+    protected float getActiveScale() {
+        // FIXME Honestly I don't understand this distinction - shouldn't  
it
+        // always be PPaintContext.CURRENT_PAINT_CONTEXT regardless of the
+        // debugging flag?
+        if (PDebug.getProcessingOutput()) {
+            if (PPaintContext.CURRENT_PAINT_CONTEXT != null) {
+                return (float)  
PPaintContext.CURRENT_PAINT_CONTEXT.getScale();
+            }
+        }
+        else {
+            if (PPickPath.CURRENT_PICK_PATH != null) {
+                return (float) PPickPath.CURRENT_PICK_PATH.getScale();
+            }
+        }
+        return 1.0f;
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (stroke == null ? 0 : stroke.hashCode());
+        return result;
+    }
+
+    /**
+     * Factory to create a new internal stroke delegate. Made protected to
+     * enable custom re-implementations.
+     */
+    protected abstract Stroke newStroke(final float activeScale);
+
+    public String toString() {
+        return stroke.toString();
+    }
+}

--~--~---------~--~----~------------~-------~--~----~
Piccolo2D Developers Group: http://groups.google.com/group/piccolo2d-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to