[skycastle-commits] SF.net SVN: skycastle: [498] trunk/skycastle/modules

  • From: zzorn@xxxxxxxxxxxxxxxxxxxxx
  • To: skycastle-commits@xxxxxxxxxxxxx
  • Date: Tue, 29 Apr 2008 03:36:16 -0700

Revision: 498
          http://skycastle.svn.sourceforge.net/skycastle/?rev=498&view=rev
Author:   zzorn
Date:     2008-04-29 03:36:15 -0700 (Tue, 29 Apr 2008)

Log Message:
-----------
Extracted stroke appearance calculation into its own class.

Implemented merging with previous point edges if current point edges overlap 
them.

Modified Paths:
--------------
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/sample/DataSample.java

Added Paths:
-----------
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/GeometryUtils.java

Added: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java
                             (rev 0)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java
     2008-04-29 10:36:15 UTC (rev 498)
@@ -0,0 +1,194 @@
+package org.skycastle.sketch.rendering;
+
+import com.jme.math.FastMath;
+import com.jme.math.Vector2f;
+import com.jme.math.Vector3f;
+import com.jme.renderer.ColorRGBA;
+import org.skycastle.sketch.model.stroke.Stroke;
+import org.skycastle.sketch.sample.DataSample;
+import org.skycastle.util.GeometryUtils;
+import org.skycastle.util.ParameterChecker;
+
+/**
+ * Calculates rendering parameters for a stroke.
+ *
+ * @author Hans Haggstrom
+ */
+public final class StrokeAppearanceCalculator
+{
+
+    //======================================================================
+    // Private Fields
+
+    private final Stroke myStroke;
+
+
+    private final DataSample myPointStatePrevious = new DataSample();
+    private final DataSample myPointStateCurrent = new DataSample();
+    private final DataSample myPointStateNext = new DataSample();
+
+    private final Vector2f myPreviousLeftEdge = new Vector2f();
+    private final Vector2f myPreviousRightEdge = new Vector2f();
+    private final Vector2f myPreviousMid = new Vector2f();
+
+    private int myCurrentIndex = 0;
+
+    //======================================================================
+    // Private Constants
+
+    private static final float DEFAULT_Z_LEVEL = 0;
+    private static final float DEFAULT_WIDTH = 50;
+    private static final float DEFAULT_RED = 0;
+    private static final float DEFAULT_GREEN = 0;
+    private static final float DEFAULT_BLUE = 0;
+    private static final float DEFAULT_ALPHA = 0.33f;
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
+    public StrokeAppearanceCalculator( final Stroke stroke )
+    {
+        ParameterChecker.checkNotNull( stroke, "stroke" );
+
+        myStroke = stroke;
+
+        resetToStart();
+    }
+
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * Moves to the start of the stroke, and does various initialization.
+     */
+    public void resetToStart()
+    {
+        myPointStatePrevious.clear();
+        myPointStateCurrent.clear();
+        myPointStateNext.clear();
+
+        myCurrentIndex = 0;
+    }
+
+    /**
+     * Fills in the given parameters with the values for the next point on the 
stroke.
+     *
+     * @return true if there are more points, false if not.
+     */
+    public boolean calculateNextPoint( Vector3f posLeft, Vector3f posMid, 
Vector3f posRight,
+                                       ColorRGBA colorLeft, ColorRGBA 
colorMid, ColorRGBA colorRight )
+    {
+        final int lastPointIndex = getLastPointIndex();
+        if ( myCurrentIndex > lastPointIndex )
+        {
+            throw new IndexOutOfBoundsException( "There are no more points" );
+        }
+
+        final DataSample strokePoint = myStroke.getPoints().get( 
myCurrentIndex );
+        final DataSample nextPoint = myStroke.getPoints().get( Math.min( 
myCurrentIndex + 1, lastPointIndex ) );
+
+        myPointStateCurrent.updateValuesWith( strokePoint );
+
+        myPointStateNext.updateValuesWith( strokePoint );
+        myPointStateNext.updateValuesWith( nextPoint );
+
+        initializePoint( myPointStatePrevious, myPointStateCurrent, 
myPointStateNext,
+                         posLeft, posMid, posRight,
+                         colorLeft, colorMid, colorRight );
+
+        myPointStatePrevious.updateValuesWith( strokePoint );
+
+        myCurrentIndex++;
+
+        return myCurrentIndex <= lastPointIndex;
+    }
+
+    private int getLastPointIndex()
+    {
+        return myStroke.getPoints().size() - 1;
+    }
+
+    //======================================================================
+    // Private Methods
+
+    @SuppressWarnings( { "MagicNumber" } )
+    private Vector2f calculateLeftEdgeOffset( final DataSample previousPoint,
+                                              final DataSample nextPoint,
+                                              final float width )
+    {
+        final Vector2f v = new Vector2f( nextPoint.getVariable( "x", 0 ),
+                                         nextPoint.getVariable( "y", 0 ) );
+        v.subtractLocal( new Vector2f( previousPoint.getVariable( "x", 0 ),
+                                       previousPoint.getVariable( "y", 0 ) ) );
+        v.normalizeLocal();
+
+        v.rotateAroundOrigin( FastMath.HALF_PI, true );
+
+        v.multLocal( 0.5f * width );
+
+        return v;
+    }
+
+
+    private void initializePoint( final DataSample previousPoint, final 
DataSample strokePoint, final DataSample nextPoint,
+                                  final Vector3f posLeft, final Vector3f 
posMid, final Vector3f posRight,
+                                  final ColorRGBA colorLeft, final ColorRGBA 
colorMid, final ColorRGBA colorRight )
+    {
+        final float xPos = strokePoint.getVariable( "x", 0 );
+        final float yPos = strokePoint.getVariable( "y", 0 );
+        final float zPos = DEFAULT_Z_LEVEL;
+        final float width = strokePoint.getVariable( "width", DEFAULT_WIDTH );
+
+        final float colorR = strokePoint.getVariable( "colorR", DEFAULT_RED );
+        final float colorG = strokePoint.getVariable( "colorG", DEFAULT_GREEN 
);
+        final float colorB = strokePoint.getVariable( "colorB", DEFAULT_BLUE );
+        final float colorA = strokePoint.getVariable( "colorA", DEFAULT_ALPHA 
);
+
+        final Vector2f mid = new Vector2f( xPos, yPos );
+
+
+        final Vector2f leftEdge = calculateLeftEdgeOffset( previousPoint, 
nextPoint, width );
+        final Vector2f rightEdge = new Vector2f( -leftEdge.x, -leftEdge.y );
+
+        leftEdge.addLocal( mid );
+        rightEdge.addLocal( mid );
+
+        // Check if either edge crosses the previous one.  In that case, use 
the previous endpoint
+        if ( linesIntersect( mid, leftEdge, myPreviousMid, myPreviousLeftEdge 
) )
+        {
+            leftEdge.set( myPreviousLeftEdge );
+        }
+        if ( linesIntersect( mid, rightEdge, myPreviousMid, 
myPreviousRightEdge ) )
+        {
+            rightEdge.set( myPreviousRightEdge );
+        }
+
+        myPreviousMid.set( mid );
+        myPreviousLeftEdge.set( leftEdge );
+        myPreviousRightEdge.set( rightEdge );
+
+
+        posMid.set( mid.x, mid.y, zPos );
+        posLeft.set( leftEdge.x, leftEdge.y, zPos );
+        posRight.set( rightEdge.x, rightEdge.y, zPos );
+
+        colorMid.set( colorR, colorG, colorB, colorA );
+        colorLeft.set( colorR, colorG, colorB, 0 );
+        colorRight.set( colorR, colorG, colorB, 0 );
+    }
+
+    private boolean linesIntersect( final Vector2f startA, final Vector2f endA,
+                                    final Vector2f startB, final Vector2f endB 
)
+    {
+        return GeometryUtils.isLineIntersectingLine( startA.x, startA.y, 
endA.x, endA.y,
+                                                     startB.x, startB.y, 
endB.x, endB.y );
+    }
+
+    public boolean hasNextPoint()
+    {
+        return myCurrentIndex <= getLastPointIndex();
+    }
+}

Added: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java
                             (rev 0)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java
     2008-04-29 10:36:15 UTC (rev 498)
@@ -0,0 +1,8 @@
+package org.skycastle.sketch.rendering;
+
+/**
+ * @author Hans Haggstrom
+ */
+public class StrokePartRenderer
+{
+}

Modified: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java
 2008-04-28 20:34:20 UTC (rev 497)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java
 2008-04-29 10:36:15 UTC (rev 498)
@@ -3,7 +3,6 @@
 import com.jme.bounding.BoundingBox;
 import com.jme.image.Image;
 import com.jme.image.Texture;
-import com.jme.math.FastMath;
 import com.jme.math.Vector2f;
 import com.jme.math.Vector3f;
 import com.jme.renderer.ColorRGBA;
@@ -45,8 +44,6 @@
 
     private final Stroke myStroke;
 
-    private final float myZ;
-
     private final Object myTextureStateLock = new Object();
 
     private FloatBuffer myVertexes;
@@ -54,11 +51,7 @@
     private FloatBuffer myTextureCoordinates;
     private FloatBuffer myNormals;
     private IntBuffer myIndices;
-    private int myNumberOfVertices;
-    private int myNumberOfIndices;
-    private int myNumberOfPoints;
     private int myNumberOfSegments;
-    private int myNumberOfTriangles;
 
     private TextureState myTextureState;
     private Texture myTexture;
@@ -82,11 +75,7 @@
                                                                                
    false );
     private static final int NUMBER_OF_TRIANGLES_PER_SEGMENT = 4;
     private static final int NUMBER_OF_INDICES_IN_SEGMENT = 3 * 
NUMBER_OF_TRIANGLES_PER_SEGMENT;
-    private static final float DEFAULT_WIDTH = 15f;
-    private static final float DEFAULT_RED = 0;
-    private static final float DEFAULT_GREEN = 0;
-    private static final float DEFAULT_BLUE = 0f;
-    private static final float DEFAULT_ALPHA = 0.33f;
+    private float myZ1;
 
     //======================================================================
     // Public Methods
@@ -104,6 +93,7 @@
         this( stroke, 0 );
     }
 
+    private final StrokeAppearanceCalculator myAppearanceCalculator;
 
     /**
      * Creates a new {@link StrokeRenderer}.
@@ -122,8 +112,11 @@
         ParameterChecker.checkNormalNumber( z, "z" );
 
         myStroke = stroke;
-        myZ = z;
+        myZ1 = z;
 
+        myAppearanceCalculator = new StrokeAppearanceCalculator( myStroke );
+
+
         myStroke.addStrokeListener( new StrokeListener()
         {
 
@@ -233,18 +226,18 @@
     private void recalculateStroke()
     {
         // Calculate sizes
-        myNumberOfPoints = myStroke.getPoints().size();
-        myNumberOfSegments = myNumberOfPoints - 1;
-        myNumberOfTriangles = myNumberOfSegments * 
NUMBER_OF_TRIANGLES_PER_SEGMENT;
-        myNumberOfVertices = myNumberOfPoints * 3;
-        myNumberOfIndices = myNumberOfSegments * NUMBER_OF_INDICES_IN_SEGMENT;
+        final int numberOfPoints = myStroke.getPoints().size();
+        myNumberOfSegments = numberOfPoints - 1;
+        final int numberOfTriangles = myNumberOfSegments * 
NUMBER_OF_TRIANGLES_PER_SEGMENT;
+        final int numberOfVertices = numberOfPoints * 3;
+        final int numberOfIndices = myNumberOfSegments * 
NUMBER_OF_INDICES_IN_SEGMENT;
 
         // Create databuffers
-        myVertexes = BufferUtils.createVector3Buffer( myNumberOfVertices );
-        myColors = BufferUtils.createColorBuffer( myNumberOfVertices );
-        myTextureCoordinates = BufferUtils.createVector2Buffer( 
myNumberOfVertices );
-        myNormals = BufferUtils.createVector3Buffer( myNumberOfVertices );
-        myIndices = BufferUtils.createIntBuffer( myNumberOfIndices );
+        myVertexes = BufferUtils.createVector3Buffer( numberOfVertices );
+        myColors = BufferUtils.createColorBuffer( numberOfVertices );
+        myTextureCoordinates = BufferUtils.createVector2Buffer( 
numberOfVertices );
+        myNormals = BufferUtils.createVector3Buffer( numberOfVertices );
+        myIndices = BufferUtils.createIntBuffer( numberOfIndices );
 
         // Stich together the vertices into triangles
         initializeIndices();
@@ -296,88 +289,29 @@
 
     private void initializeVetices()
     {
-        final DataSample pointStatePrevious = new DataSample();
-        final DataSample pointState = new DataSample();
-        final DataSample pointStateNext = new DataSample();
+        myAppearanceCalculator.resetToStart();
 
-        int pointIndex = 0;
-        final int numberOfPoints = myStroke.getPoints().size();
-        for ( int i = 0; i < numberOfPoints; i++ )
+        int index = 0;
+        while ( myAppearanceCalculator.hasNextPoint() )
         {
-            final int nextIndex = Math.min( i + 1, numberOfPoints - 1 );
+            final Vector3f positionLeft = new Vector3f();
+            final Vector3f positionMid = new Vector3f();
+            final Vector3f positionRight = new Vector3f();
 
-            final DataSample strokePoint = myStroke.getPoints().get( i );
-            final DataSample nextPoint = myStroke.getPoints().get( nextIndex );
+            final ColorRGBA colorLeft = new ColorRGBA();
+            final ColorRGBA colorMid = new ColorRGBA();
+            final ColorRGBA colorRight = new ColorRGBA();
 
-            pointStateNext.updateValuesWith( strokePoint );
-            pointStateNext.updateValuesWith( nextPoint );
+            myAppearanceCalculator.calculateNextPoint( positionLeft, 
positionMid, positionRight,
+                                                       colorLeft, colorMid, 
colorRight );
 
-            pointState.updateValuesWith( strokePoint );
-
-            initializePoint( pointState, pointIndex++, pointStatePrevious, 
pointStateNext );
-
-            pointStatePrevious.updateValuesWith( strokePoint );
+            setPointData( index++, positionLeft, colorLeft );
+            setPointData( index++, positionMid, colorMid );
+            setPointData( index++, positionRight, colorRight );
         }
     }
 
 
-    @SuppressWarnings( { "PointlessArithmeticExpression" } )
-    private void initializePoint( final DataSample strokePoint,
-                                  final int pointIndex,
-                                  final DataSample previousPoint,
-                                  final DataSample nextPoint )
-    {
-        final int index = pointIndex * 3;
-
-        final float xPos = strokePoint.getVariable( "x", 0 );
-        final float yPos = strokePoint.getVariable( "y", 0 );
-        final float zPos = myZ;
-        final float width = strokePoint.getVariable( "width", DEFAULT_WIDTH );
-
-        final float colorR = strokePoint.getVariable( "colorR", DEFAULT_RED );
-        final float colorG = strokePoint.getVariable( "colorG", DEFAULT_GREEN 
);
-        final float colorB = strokePoint.getVariable( "colorB", DEFAULT_BLUE );
-        final float colorA = strokePoint.getVariable( "colorA", DEFAULT_ALPHA 
);
-
-
-        final Vector3f positionMid = new Vector3f( xPos, yPos, zPos );
-        final ColorRGBA colorMid = new ColorRGBA( colorR, colorG, colorB, 
colorA );
-
-
-        final Vector2f leftEdgeOffset = calculateLeftEdgeOffset( 
previousPoint, nextPoint, width );
-
-        final ColorRGBA colorLeft = new ColorRGBA( colorR, colorG, colorB, 0 );
-        final Vector3f positionLeft = new Vector3f( positionMid );
-        positionLeft.addLocal( leftEdgeOffset.x, leftEdgeOffset.y, 0 );
-
-        final ColorRGBA colorRight = new ColorRGBA( colorR, colorG, colorB, 0 
);
-        final Vector3f positionRight = new Vector3f( positionMid );
-        positionRight.addLocal( -leftEdgeOffset.x, -leftEdgeOffset.y, 0 );
-
-        setPointData( index + 0, positionLeft, colorLeft );
-        setPointData( index + 1, positionMid, colorMid );
-        setPointData( index + 2, positionRight, colorRight );
-    }
-
-
-    private Vector2f calculateLeftEdgeOffset( final DataSample previousPoint,
-                                              final DataSample nextPoint,
-                                              final float width )
-    {
-        final Vector2f v = new Vector2f( nextPoint.getVariable( "x", 0 ),
-                                         nextPoint.getVariable( "y", 0 ) );
-        v.subtractLocal( new Vector2f( previousPoint.getVariable( "x", 0 ),
-                                       previousPoint.getVariable( "y", 0 ) ) );
-        v.normalizeLocal();
-
-        v.rotateAroundOrigin( FastMath.HALF_PI, true );
-
-        v.multLocal( 0.5f * width );
-
-        return v;
-    }
-
-
     private void setPointData( final int index, final Vector3f position, final 
ColorRGBA color )
     {
         BufferUtils.setInBuffer( position, myVertexes, index );

Modified: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/sample/DataSample.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/sample/DataSample.java
        2008-04-28 20:34:20 UTC (rev 497)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/sample/DataSample.java
        2008-04-29 10:36:15 UTC (rev 498)
@@ -204,4 +204,11 @@
         };
     }
 
+    /**
+     * Removes all variable values from this {@link DataSample}.
+     */
+    public void clear()
+    {
+        myVariables.clear();
+    }
 }

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/GeometryUtils.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/GeometryUtils.java
                           (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/GeometryUtils.java
   2008-04-29 10:36:15 UTC (rev 498)
@@ -0,0 +1,108 @@
+package org.skycastle.util;
+
+
+/**
+ * Collection of geometry utility methods.
+ * <p/>
+ * Some of the source is adapted from http://geosoft.no/software/index.html 
(LGPL licensed).
+ *
+ * @author Hans Haggstrom
+ */
+public final class GeometryUtils
+{
+
+    //----------------------------------------------------------------------
+    // Static Methods
+
+    /**
+     * @return true if c is between a and b.
+     */
+    @SuppressWarnings( { "JavaDoc" } )
+    public static boolean isBetween( float a, float b, float c )
+    {
+        return b > a ? c >= a && c <= b : c >= b && c <= a;
+    }
+
+
+    /**
+     * Check if two line segments intersects.
+     *
+     * @param ax0 First line start x.
+     * @param ay0 First line start y.
+     * @param ax1 First line end x.
+     * @param ay1 First line end y.
+     * @param bx0 Second line start x.
+     * @param by0 Second line start y.
+     * @param bx1 Second line end x.
+     * @param by1 Second line end y.
+     *
+     * @return True if the two lines intersects.
+     */
+    public static boolean isLineIntersectingLine( float ax0, float ay0, float 
ax1, float ay1,
+                                                  float bx0, float by0, float 
bx1, float by1 )
+    {
+        int s1 = sameSide( ax0, ay0, ax1, ay1, bx0, by0, bx1, by1 );
+        int s2 = sameSide( bx0, by0, bx1, by1, ax0, ay0, ax1, ay1 );
+
+        return s1 <= 0 && s2 <= 0;
+    }
+
+
+    /**
+     * Check if two points are on the same side of a given line.
+     * Algorithm from Sedgewick page 350.
+     *
+     * @param x0  Line start x.
+     * @param y0  Line start y.
+     * @param x1  Line end x.
+     * @param y1  Line end y.
+     * @param px0 First point x.
+     * @param py0 First point y.
+     * @param px1 Second point x.
+     * @param py1 Second point y.
+     *
+     * @return < 0 if points on opposite sides, <br>
+     *         = 0 if one of the points is exactly on the line, or <br>
+     *         > 0 if points on same side. <br>
+     */
+    public static int sameSide( float x0, float y0, float x1, float y1,
+                                float px0, float py0, float px1, float py1 )
+    {
+        int sameSide = 0;
+
+        float dx = x1 - x0;
+        float dy = y1 - y0;
+        float dx1 = px0 - x0;
+        float dy1 = py0 - y0;
+        float dx2 = px1 - x1;
+        float dy2 = py1 - y1;
+
+        // Cross product of the vector from the endpoint of the line to the 
point
+        float c1 = dx * dy1 - dy * dx1;
+        float c2 = dx * dy2 - dy * dx2;
+
+        if ( c1 != 0 && c2 != 0 )
+        {
+            sameSide = c1 < 0 == c2 < 0 ? 1 : -1;
+        }
+        else if ( dx == 0 && dx1 == 0 && dx2 == 0 )
+        {
+            sameSide = !isBetween( y0, y1, py0 ) && !isBetween( y0, y1, py1 ) 
? 1 : 0;
+        }
+        else if ( dy == 0 && dy1 == 0 && dy2 == 0 )
+        {
+            sameSide = !isBetween( x0, x1, px0 ) && !isBetween( x0, x1, px1 ) 
? 1 : 0;
+        }
+
+        return sameSide;
+    }
+
+    //======================================================================
+    // Private Methods
+
+    private GeometryUtils()
+    {
+    }
+
+}
+


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

Other related posts:

  • » [skycastle-commits] SF.net SVN: skycastle: [498] trunk/skycastle/modules