[Arakhnę-Dev] [418] * Update the intersection tests between two paths.

[ Thread Index | Date Index | More arakhne.org/dev Archives ]


Revision: 418
Author:   galland
Date:     2013-04-08 11:09:17 +0200 (Mon, 08 Apr 2013)
Log Message:
-----------
* Update the intersection tests between two paths.
* Update the intersection tests between a path and a path iterator.
* Adding unit tests for intersection tests between two paths and path iterators.

Modified Paths:
--------------
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractPath2fTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/ClosePath2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/OpenPath2fTest.java

Added Paths:
-----------
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/GeneratePictureForTest.java

Modified: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java	2013-04-08 09:07:54 UTC (rev 417)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -89,17 +89,17 @@
 	 */
 	public static boolean contains(PathIterator2f pi, float rx, float ry, float rwidth, float rheight) {
 		// Copied from AWT API
-        if (rwidth <= 0 || rheight <= 0) {
-            return false;
-        }
-        int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
-        int crossings = computeCrossingsFromRect(
-        		pi, 
-        		rx, ry, rx+rwidth, ry+rheight,
-        		false,
-        		true);
-        return (crossings != MathConstants.SHAPE_INTERSECTS &&
-                (crossings & mask) != 0);
+		if (rwidth <= 0 || rheight <= 0) {
+			return false;
+		}
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromRect(
+				pi, 
+				rx, ry, rx+rwidth, ry+rheight,
+				false,
+				true);
+		return (crossings != MathConstants.SHAPE_INTERSECTS &&
+				(crossings & mask) != 0);
 	}
 
 	/**
@@ -163,7 +163,7 @@
 	public static int computeCrossingsFromPoint(PathIterator2f pi, float px, float py) {
 		return computeCrossingsFromPoint(pi, px, py, true, true);
 	}
-	
+
 	/**
 	 * Calculates the number of times the given path
 	 * crosses the ray extending to the right from (px,py).
@@ -283,11 +283,11 @@
 			default:
 			}
 		}
-		
+
 		assert(crossings!=MathConstants.SHAPE_INTERSECTS);
 
 		boolean isOpen = (curx != movx) || (cury != movy);
-		
+
 		if (isOpen) {
 			if (closeable) {
 				// Not closed
@@ -322,7 +322,7 @@
 	public static int computeCrossingsFromEllipse(PathIterator2f pi, float ex, float ey, float ew, float eh) {
 		return computeCrossingsFromEllipse(0, pi, ex, ey, ew, eh, true, true);
 	}
-	
+
 	/**
 	 * Calculates the number of times the given path
 	 * crosses the given ellipse extending to the right.
@@ -460,7 +460,7 @@
 				numCrosses = 0;
 			}
 		}
-		
+
 		return numCrosses;
 	}
 
@@ -477,7 +477,7 @@
 	public static int computeCrossingsFromCircle(PathIterator2f pi, float cx, float cy, float radius) {
 		return computeCrossingsFromCircle(0, pi, cx, cy, radius, true, true);
 	}
-	
+
 	/**
 	 * Calculates the number of times the given path
 	 * crosses the given circle extending to the right.
@@ -594,7 +594,7 @@
 			default:
 			}
 		}
-		
+
 		assert(numCrosses!=MathConstants.SHAPE_INTERSECTS);
 
 		boolean isOpen = (curx != movx) || (cury != movy);
@@ -614,7 +614,7 @@
 				numCrosses = 0;
 			}
 		}
-		
+
 		return numCrosses;
 	}
 
@@ -632,10 +632,10 @@
 	public static int computeCrossingsFromSegment(PathIterator2f pi, float x1, float y1, float x2, float y2) {
 		return computeCrossingsFromSegment(0, pi, x1, y1, x2, y2, true);
 	}
-	
+
 	/**
 	 * Calculates the number of times the given path
-	 * crosses the given circle extending to the right.
+	 * crosses the given segment extending to the right.
 	 * 
 	 * @param crossings is the initial value for crossing.
 	 * @param pi is the description of the path.
@@ -740,9 +740,9 @@
 		}
 
 		assert(numCrosses!=MathConstants.SHAPE_INTERSECTS);
-		
+
 		boolean isOpen = (curx != movx) || (cury != movy);
-		
+
 		if (isOpen) {
 			if (closeable) {
 				numCrosses = Segment2f.computeCrossingsFromSegment(
@@ -758,17 +758,17 @@
 				numCrosses = 0;
 			}
 		}
-		
+
 		return numCrosses;
 	}
 
 	/**
 	 * Accumulate the number of times the path crosses the shadow
 	 * extending to the right of the rectangle.  See the comment
-	 * for the RECT_INTERSECTS constant for more complete details.
+	 * for the SHAPE_INTERSECTS constant for more complete details.
 	 * The return value is the sum of all crossings for both the
 	 * top and bottom of the shadow for every segment in the path,
-	 * or the special value RECT_INTERSECTS if the path ever enters
+	 * or the special value SHAPE_INTERSECTS if the path ever enters
 	 * the interior of the rectangle.
 	 * The path must start with a SEG_MOVETO, otherwise an exception is
 	 * thrown.
@@ -790,10 +790,10 @@
 	/**
 	 * Accumulate the number of times the path crosses the shadow
 	 * extending to the right of the rectangle.  See the comment
-	 * for the RECT_INTERSECTS constant for more complete details.
+	 * for the SHAPE_INTERSECTS constant for more complete details.
 	 * The return value is the sum of all crossings for both the
 	 * top and bottom of the shadow for every segment in the path,
-	 * or the special value RECT_INTERSECTS if the path ever enters
+	 * or the special value SHAPE_INTERSECTS if the path ever enters
 	 * the interior of the rectangle.
 	 * The path must start with a SEG_MOVETO, otherwise an exception is
 	 * thrown.
@@ -911,11 +911,11 @@
 			default:
 			}
 		}
-		
+
 		assert(crossings != MathConstants.SHAPE_INTERSECTS);
 
 		boolean isOpen = (curx != movx) || (cury != movy);
-		
+
 		if (isOpen) {
 			if (closeable) {
 				// Not closed
@@ -931,7 +931,7 @@
 				crossings = 0;
 			}
 		}
-		
+
 		return crossings;
 	}
 
@@ -954,7 +954,7 @@
 	/** Winding rule for the path.
 	 */
 	PathWindingRule windingRule;
-	
+
 	/** Indicates if the path is empty.
 	 * The path is empty when there is no point inside, or
 	 * all the points are at the same coordinate, or
@@ -962,11 +962,19 @@
 	 * (a path with a line or a curve).
 	 */
 	private Boolean isEmpty = Boolean.TRUE;
-	
-	/** Buffer for the bounds of the path.
+
+	/** Buffer for the bounds of the path that corresponds
+	 * to the points really on the path (eg, the pixels
+	 * drawn). The control points of the curves are
+	 * not considered in this bounds.
 	 */
-	private SoftReference<Rectangle2f> bounds = null;
+	private SoftReference<Rectangle2f> graphicalBounds = null;
 
+	/** Buffer for the bounds of the path that corresponds
+	 * to all the points added in the path.
+	 */
+	private SoftReference<Rectangle2f> logicalBounds = null;
+
 	/**
 	 */
 	public Path2f() {
@@ -1001,7 +1009,7 @@
 		this.windingRule = windingRule;
 		add(iterator);
 	}
-	
+
 	@Override
 	public void clear() {
 		this.types = new PathElementType[GROW_SIZE];
@@ -1010,9 +1018,10 @@
 		this.numCoords = 0;
 		this.numTypes = 0;
 		this.isEmpty = true;
-		this.bounds = null;
+		this.graphicalBounds = null;
+		this.logicalBounds = null;
 	}
-	
+
 	@Override
 	public String toString() {
 		StringBuilder b = new StringBuilder();
@@ -1112,7 +1121,8 @@
 			this.coords[this.numCoords++] = x;
 			this.coords[this.numCoords++] = y;
 		}
-		this.bounds = null;
+		this.graphicalBounds = null;
+		this.logicalBounds = null;
 	}
 
 	/**
@@ -1129,7 +1139,8 @@
 		this.coords[this.numCoords++] = x;
 		this.coords[this.numCoords++] = y;
 		this.isEmpty = null;
-		this.bounds = null;
+		this.graphicalBounds = null;
+		this.logicalBounds = null;
 	}
 
 	/**
@@ -1153,7 +1164,8 @@
 		this.coords[this.numCoords++] = x2;
 		this.coords[this.numCoords++] = y2;
 		this.isEmpty = null;
-		this.bounds = null;
+		this.graphicalBounds = null;
+		this.logicalBounds = null;
 	}
 
 	/**
@@ -1183,7 +1195,8 @@
 		this.coords[this.numCoords++] = x3;
 		this.coords[this.numCoords++] = y3;
 		this.isEmpty = null;
-		this.bounds = null;
+		this.graphicalBounds = null;
+		this.logicalBounds = null;
 	}
 
 	/**
@@ -1194,8 +1207,8 @@
 	 */
 	public void closePath() {
 		if (this.numTypes<=0 ||
-			(this.types[this.numTypes-1]!=PathElementType.CLOSE
-			&&this.types[this.numTypes-1]!=PathElementType.MOVE_TO)) {
+				(this.types[this.numTypes-1]!=PathElementType.CLOSE
+				&&this.types[this.numTypes-1]!=PathElementType.MOVE_TO)) {
 			ensureSlots(true, 0);
 			this.types[this.numTypes++] = PathElementType.CLOSE;
 		}
@@ -1277,7 +1290,8 @@
 				this.coords[i++] = p.getX();
 				this.coords[i++] = p.getY();
 			}
-			this.bounds = null;
+			this.graphicalBounds = null;
+			this.logicalBounds = null;
 		}
 	}
 
@@ -1289,8 +1303,11 @@
 			this.coords[i++] += dx;
 			this.coords[i++] += dy;
 		}
-		Rectangle2f bb = this.bounds==null ? null : this.bounds.get();
+		Rectangle2f bb;
+		bb = this.logicalBounds==null ? null : this.logicalBounds.get();
 		if (bb!=null) bb.translate(dx, dy);
+		bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
+		if (bb!=null) bb.translate(dx, dy);
 	}
 
 	/** {@inheritDoc}
@@ -1419,34 +1436,189 @@
 		return (crossings == MathConstants.SHAPE_INTERSECTS ||
 				(crossings & mask) != 0);
 	}
-	
+
 	@Override
 	public boolean intersects(Path2f s) {
-		if (s.size()>size())
-			return intersects(s.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO), getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO));
-		return intersects(getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO), s.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO));
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromPath(
+				s.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+				new PathShadow(this),
+				false,
+				true);
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
 	}
-	
-	private static boolean intersects(PathIterator2f iterator1, PathIterator2f iterator2) {
-		int mask = (iterator1.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+
+	@Override
+	public boolean intersects(PathIterator2f s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromPath(
+				s,
+				new PathShadow(this),
+				false,
+				true);
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	/**
+	 * Accumulate the number of times the path crosses the shadow
+	 * extending to the right of the second path.  See the comment
+	 * for the SHAPE_INTERSECTS constant for more complete details.
+	 * The return value is the sum of all crossings for both the
+	 * top and bottom of the shadow for every segment in the path,
+	 * or the special value SHAPE_INTERSECTS if the path ever enters
+	 * the interior of the rectangle.
+	 * The path must start with a SEG_MOVETO, otherwise an exception is
+	 * thrown.
+	 * The caller must check r[xy]{min,max} for NaN values.
+	 * 
+	 * @param iterator1 is the iterator on the path elements.
+	 * @param shadow is the description of the shape to project to the right.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @param onlyIntersectWhenOpen indicates if the crossings is set to 0 when
+	 * the path is open and there is not SHAPE_INTERSECT.
+	 * @return the crossings.
+	 * @see Weiler–Atherton clipping algorithm 
+	 */
+	static int computeCrossingsFromPath(
+			PathIterator2f iterator1, 
+			PathShadow shadow,
+			boolean closeable,
+			boolean onlyIntersectWhenOpen) {
+		if (!iterator1.hasNext()) return 0;
+
+		PathElement2f pathElement1 = iterator1.next();
+
+		if (pathElement1.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in the first path definition"); //$NON-NLS-1$
+		}
+
 		Path2f subPath;
+		float curx, cury, movx, movy, endx, endy;
+		curx = movx = pathElement1.toX;
+		cury = movy = pathElement1.toY;
+		int crossings = 0;
+		int n;
+
+		while (crossings != MathConstants.SHAPE_INTERSECTS
+				&& iterator1.hasNext()) {
+			pathElement1 = iterator1.next();
+			switch (pathElement1.type) {
+			case MOVE_TO:
+				// Count should always be a multiple of 2 here.
+				// assert((crossings & 1) != 0);
+				movx = curx = pathElement1.toX;
+				movy = cury = pathElement1.toY;
+				break;
+			case LINE_TO:
+				endx = pathElement1.toX;
+				endy = pathElement1.toY;
+				crossings = shadow.computeCrossings(crossings,
+						curx, cury,
+						endx, endy);
+				if (crossings==MathConstants.SHAPE_INTERSECTS)
+					return crossings;
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+				endx = pathElement1.toX;
+				endy = pathElement1.toY;
+				subPath = new Path2f();
+				subPath.moveTo(curx, cury);
+				subPath.quadTo(
+						pathElement1.ctrlX1, pathElement1.ctrlY1,
+						endx, endy);
+				n = computeCrossingsFromPath(
+						subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						shadow,
+						false,
+						false);
+				if (n==MathConstants.SHAPE_INTERSECTS)
+					return n;
+				crossings += n;
+				curx = endx;
+				cury = endy;
+				break;
+			case CURVE_TO:
+				endx = pathElement1.toX;
+				endy = pathElement1.toY;
+				subPath = new Path2f();
+				subPath.moveTo(curx, cury);
+				subPath.curveTo(
+						pathElement1.ctrlX1, pathElement1.ctrlY1,
+						pathElement1.ctrlX2, pathElement1.ctrlY2,
+						endx, endy);
+				n = computeCrossingsFromPath(
+						subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						shadow,
+						false,
+						false);
+				if (n==MathConstants.SHAPE_INTERSECTS)
+					return n;
+				crossings += n;
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (curx != movx || cury != movy) {
+					crossings = shadow.computeCrossings(crossings,
+							curx, cury,
+							movx, movy);
+				}
+				// Stop as soon as possible
+				if (crossings!=0) return crossings;
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		assert(crossings != MathConstants.SHAPE_INTERSECTS);
+
+		boolean isOpen = (curx != movx) || (cury != movy);
+
+		if (isOpen) {
+			if (closeable) {
+				// Not closed
+				crossings = shadow.computeCrossings(crossings,
+						curx, cury,
+						movx, movy);
+			}
+			else if (onlyIntersectWhenOpen) {
+				// Assume that when is the path is open, only
+				// SHAPE_INTERSECTS may be return
+				crossings = 0;
+			}
+		}
+
+		return crossings;
+	}
+
+	private static boolean buildGraphicalBoundingBox(PathIterator2f iterator, Rectangle2f box) {
+		boolean foundOneLine = false;
+		float xmin = Float.POSITIVE_INFINITY;
+		float ymin = Float.POSITIVE_INFINITY;
+		float xmax = Float.NEGATIVE_INFINITY;
+		float ymax = Float.NEGATIVE_INFINITY;
 		PathElement2f element;
-		while (iterator1.hasNext()) {
-			element = iterator1.next();
+		Path2f subPath;
+		while (iterator.hasNext()) {
+			element = iterator.next();
 			switch(element.type) {
-			case CLOSE:
 			case LINE_TO:
-				int crossings = computeCrossingsFromSegment(
-						iterator2,
-						element.fromX, element.fromY,
-						element.toX, element.toY);
-				if (crossings == MathConstants.SHAPE_INTERSECTS ||
-					(crossings & mask) != 0) {
-					return true;
-				}
+				if (element.fromX<xmin) xmin = element.fromX;
+				if (element.fromY<ymin) ymin = element.fromY;
+				if (element.fromX>xmax) xmax = element.fromX;
+				if (element.fromY>ymax) ymax = element.fromY;
+				if (element.toX<xmin) xmin = element.toX;
+				if (element.toY<ymin) ymin = element.toY;
+				if (element.toX>xmax) xmax = element.toX;
+				if (element.toY>ymax) ymax = element.toY;
+				foundOneLine = true;
 				break;
-			case MOVE_TO:
-				break;
 			case CURVE_TO:
 				subPath = new Path2f();
 				subPath.moveTo(element.fromX, element.fromY);
@@ -1454,10 +1626,14 @@
 						element.ctrlX1, element.ctrlY1,
 						element.ctrlX2, element.ctrlY2,
 						element.toX, element.toY);
-				if (intersects(
+				if (buildGraphicalBoundingBox(
 						subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
-						iterator2)) {
-					return true;
+						box)) {
+					if (box.getMinX()<xmin) xmin = box.getMinX();
+					if (box.getMinY()<ymin) ymin = box.getMinY();
+					if (box.getMaxX()>xmax) xmax = box.getMaxX();
+					if (box.getMinX()>ymax) ymax = box.getMinX();
+					foundOneLine = true;
 				}
 				break;
 			case QUAD_TO:
@@ -1466,28 +1642,32 @@
 				subPath.quadTo(
 						element.ctrlX1, element.ctrlY1,
 						element.toX, element.toY);
-				if (intersects(
+				if (buildGraphicalBoundingBox(
 						subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
-						iterator2)) {
-					return true;
+						box)) {
+					if (box.getMinX()<xmin) xmin = box.getMinX();
+					if (box.getMinY()<ymin) ymin = box.getMinY();
+					if (box.getMaxX()>xmax) xmax = box.getMaxX();
+					if (box.getMinX()>ymax) ymax = box.getMinX();
+					foundOneLine = true;
 				}
 				break;
+			case MOVE_TO:
+			case CLOSE:
 			default:
-				throw new IllegalStateException();
 			}
 		}
-		return false;
+		if (foundOneLine) {
+			box.setFromCorners(xmin, ymin, xmax, ymax);
+		}
+		else {
+			box.clear();
+		}
+		return foundOneLine;
 	}
-	
-	@Override
-	public boolean intersects(PathIterator2f s) {
-		return intersects(getPathIterator(), s);
-	}
 
-	@Override
-	public Rectangle2f toBoundingBox() {
-		Rectangle2f bb = this.bounds==null ? null : this.bounds.get();
-		if (bb==null) {
+	private boolean buildLogicalBoundingBox(Rectangle2f box) {
+		if (this.numCoords>0) {
 			float xmin = Float.POSITIVE_INFINITY;
 			float ymin = Float.POSITIVE_INFINITY;
 			float xmax = Float.NEGATIVE_INFINITY;
@@ -1498,34 +1678,89 @@
 				if (this.coords[i]>xmax) xmax = this.coords[i];
 				if (this.coords[i+1]>ymax) ymax = this.coords[i+1];
 			}
+			box.setFromCorners(xmin,  ymin, xmax, ymax);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * <p>
+	 * The replied bounding box does not consider the control points
+	 * of the path. Only the "visible" points are considered.
+	 * 
+	 * @see #toBoundingBoxWithCtrlPoints()
+	 */
+	@Override
+	public Rectangle2f toBoundingBox() {
+		Rectangle2f bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
+		if (bb==null) {
 			bb = new Rectangle2f();
-			bb.setFromCorners(xmin, ymin, xmax, ymax);
-			this.bounds = new SoftReference<Rectangle2f>(bb);
+			buildGraphicalBoundingBox(
+					getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+					bb);
+			this.graphicalBounds = new SoftReference<Rectangle2f>(bb);
 		}
 		return bb.clone();
 	}
 
+	/** Replies the bounding box of all the points added in this path.
+	 * <p>
+	 * The replied bounding box includes the (invisible) control points.
+	 * 
+	 * @return the bounding box with the control points.
+	 * @see #toBoundingBox()
+	 */
+	public Rectangle2f toBoundingBoxWithCtrlPoints() {
+		Rectangle2f bb = this.logicalBounds==null ? null : this.logicalBounds.get();
+		if (bb==null) {
+			bb = new Rectangle2f();
+			buildLogicalBoundingBox(bb);
+			this.logicalBounds = new SoftReference<Rectangle2f>(bb);
+		}
+		return bb.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * <p>
+	 * The replied bounding box does not consider the control points
+	 * of the path. Only the "visible" points are considered.
+	 * 
+	 * @see #toBoundingBoxWithCtrlPoints(Rectangle2f)
+	 */
 	@Override
 	public void toBoundingBox(Rectangle2f box) {
-		Rectangle2f bb = this.bounds==null ? null : this.bounds.get();
+		Rectangle2f bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
 		if (bb==null) {
-			float xmin = Float.POSITIVE_INFINITY;
-			float ymin = Float.POSITIVE_INFINITY;
-			float xmax = Float.NEGATIVE_INFINITY;
-			float ymax = Float.NEGATIVE_INFINITY;
-			for(int i=0; i<this.numCoords; i+= 2) {
-				if (this.coords[i]<xmin) xmin = this.coords[i];
-				if (this.coords[i+1]<ymin) ymin = this.coords[i+1];
-				if (this.coords[i]>xmax) xmax = this.coords[i];
-				if (this.coords[i+1]>ymax) ymax = this.coords[i+1];
-			}
 			bb = new Rectangle2f();
-			bb.setFromCorners(xmin, ymin, xmax, ymax);
-			this.bounds = new SoftReference<Rectangle2f>(bb);
+			buildGraphicalBoundingBox(
+					getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+					bb);
+			this.graphicalBounds = new SoftReference<Rectangle2f>(bb);
 		}
 		box.set(bb);
 	}
 
+	/** Compute the bounding box of all the points added in this path.
+	 * <p>
+	 * The replied bounding box includes the (invisible) control points.
+	 * 
+	 * @param box is the rectangle to set with the bounds.
+	 * @return the bounding box with the control points.
+	 * @see #toBoundingBox()
+	 */
+	public void toBoundingBoxWithCtrlPoints(Rectangle2f box) {
+		Rectangle2f bb = this.logicalBounds==null ? null : this.logicalBounds.get();
+		if (bb==null) {
+			bb = new Rectangle2f();
+			buildLogicalBoundingBox(bb);
+			this.logicalBounds = new SoftReference<Rectangle2f>(bb);
+		}
+		box.set(bb);
+	}
+
 	@Override
 	public Point2D getClosestPointTo(Point2D p) {
 		Point2D closest = null;
@@ -1536,7 +1771,7 @@
 
 		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 1);
 		int crossings = 0;
-		
+
 		while (pi.hasNext()) {
 			pe = pi.next();
 
@@ -1597,7 +1832,7 @@
 				}
 			}
 		}
-		
+
 		return closest;
 	}
 
@@ -1613,7 +1848,7 @@
 		}
 		return false;
 	}
-	
+
 	@Override
 	public int hashCode() {
 		long bits = 1L;
@@ -1690,7 +1925,7 @@
 		}
 		return clone;
 	}
-	
+
 	/** Replies the points of this path in an array.
 	 * 
 	 * @return the points.
@@ -1741,7 +1976,7 @@
 	public float getCoordAt(int index) {
 		return this.coords[index];
 	}
-	
+
 	/** Replies the point at the given index.
 	 * The index is in [0;{@link #size()}).
 	 *
@@ -1761,7 +1996,7 @@
 	public int size() {
 		return this.numCoords/2;
 	}
-	
+
 	/** Replies if this path is empty.
 	 * The path is empty when there is no point inside, or
 	 * all the points are at the same coordinate, or
@@ -1786,7 +2021,7 @@
 		}
 		return this.isEmpty;
 	}
-	
+
 	/** Replies if the given points exists in the coordinates of this path.
 	 * 
 	 * @param p
@@ -1803,8 +2038,8 @@
 		}
 		return false;
 	}
-	
-	
+
+
 	/** Remove the point with the given coordinates.
 	 * 
 	 * @param x
@@ -1828,8 +2063,8 @@
 				break;
 			case CURVE_TO:
 				if ((x==this.coords[i] && y==this.coords[i+1])
-					||(x==this.coords[i+2] && y==this.coords[i+3])
-					||(x==this.coords[i+4] && y==this.coords[i+5])) {
+						||(x==this.coords[i+2] && y==this.coords[i+3])
+						||(x==this.coords[i+4] && y==this.coords[i+5])) {
 					this.numCoords -= 6;
 					--this.numTypes;
 					System.arraycopy(this.coords, i+6, this.coords, i, this.numCoords);
@@ -1841,7 +2076,7 @@
 				break;
 			case QUAD_TO:
 				if ((x==this.coords[i] && y==this.coords[i+1])
-					||(x==this.coords[i+2] && y==this.coords[i+3])) {
+						||(x==this.coords[i+2] && y==this.coords[i+3])) {
 					this.numCoords -= 4;
 					--this.numTypes;
 					System.arraycopy(this.coords, i+4, this.coords, i, this.numCoords);
@@ -1860,7 +2095,7 @@
 		}
 		return false;
 	}
-	
+
 	/** Remove the last action.
 	 */
 	public void removeLast() {
@@ -1884,7 +2119,8 @@
 			}
 			--this.numTypes;
 			this.isEmpty = null;
-			this.bounds = null;
+			this.graphicalBounds = null;
+			this.logicalBounds = null;
 		}
 	}
 
@@ -1897,7 +2133,8 @@
 		if (this.numCoords>=2) {
 			this.coords[this.numCoords-2] = x;
 			this.coords[this.numCoords-1] = y;
-			this.bounds = null;
+			this.graphicalBounds = null;
+			this.logicalBounds = null;
 		}
 	}
 
@@ -2005,9 +2242,9 @@
 			}
 			if (element==null)
 				throw new NoSuchElementException();
-			
+
 			++this.iType;
-			
+
 			return element;
 		}
 
@@ -2161,7 +2398,7 @@
 
 		/** The source iterator.
 		 */
-		private final Iterator<PathElement2f> pathIterator;
+		private final PathIterator2f pathIterator;
 
 		/**
 		 * Square of the flatness parameter for testing against squared lengths.
@@ -2246,7 +2483,7 @@
 		 * @param limit the maximum number of recursive subdivisions
 		 * allowed for any curved segment
 		 */
-		public FlatteningPathIterator(PathWindingRule windingRule, Iterator<PathElement2f> pathIterator, float flatness, int limit) {
+		public FlatteningPathIterator(PathWindingRule windingRule, PathIterator2f pathIterator, float flatness, int limit) {
 			assert(windingRule!=null);
 			assert(flatness>=0f);
 			assert(limit>=0);
@@ -2644,7 +2881,7 @@
 		public PathWindingRule getWindingRule() {
 			return this.windingRule;
 		}
-
+		
 	} // class FlatteningPathIterator
 
 	/** An collection of the points of the path.
@@ -2727,7 +2964,7 @@
 		public boolean containsAll(Collection<?> c) {
 			for(Object obj : c) {
 				if ((!(obj instanceof Point2D))
-					||(!Path2f.this.containsPoint((Point2D)obj))) {
+						||(!Path2f.this.containsPoint((Point2D)obj))) {
 					return false;
 				}
 			}
@@ -2768,9 +3005,9 @@
 		public void clear() {
 			Path2f.this.clear();
 		}
-		
+
 	} // class PointCollection
-	
+
 	/** Iterator on the points of the path.
 	 *
 	 * @author $Author: galland$
@@ -2782,7 +3019,7 @@
 
 		private int index = 0;
 		private Point2D lastReplied = null;
-		
+
 		/**
 		 */
 		public PointIterator() {
@@ -2813,7 +3050,432 @@
 				throw new NoSuchElementException();
 			Path2f.this.remove(p.getX(), p.getY());
 		}
+
+	}
+
+	/** Shadow of a path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	static class PathShadow {
+
+		private final Path2f path;
+		private final Rectangle2f bounds;
+
+		public PathShadow(Path2f path) {
+			this.path = path;
+			this.bounds = this.path.toBoundingBox();
+		}
+
+		/** Compute the crossings between this shadow and
+		 * the given segment.
+		 * 
+		 * @param crossings is the initial value of the crossings.
+		 * @param x0 is the first point of the segment.
+		 * @param y0 is the first point of the segment.
+		 * @param x1 is the second point of the segment.
+		 * @param y1 is the second point of the segment.
+		 * @return the crossings or {@link MathConstants#SHAPE_INTERSECTS}.
+		 */
+		public int computeCrossings(
+				int crossings,
+				float x0, float y0,
+				float x1, float y1) {
+			if (this.bounds==null) return crossings;
+
+			int numCrosses = 
+					Segment2f.computeCrossingsFromRect(crossings,
+							this.bounds.getMinX(),
+							this.bounds.getMinY(),
+							this.bounds.getMaxX(),
+							this.bounds.getMaxY(),
+							x0, y0, 
+							x1, y1);
+
+			if (numCrosses==MathConstants.SHAPE_INTERSECTS) {
+				// The segment is intersecting the bounds of the shadow path.
+				// We must consider the shape of shadow path now.
+				PathShadowData data = new PathShadowData(
+						this.bounds.getMaxX(),
+						this.bounds.getMinY(),
+						this.bounds.getMaxY());
+				
+				computeCrossings1(
+						this.path.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						x0, y0, x1, y1,
+						false,
+						data);
+				numCrosses = data.crossings;
+				
+				int mask = (this.path.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+				if (numCrosses == MathConstants.SHAPE_INTERSECTS ||
+					(numCrosses & mask) != 0) {
+					// The given line is intersecting the path shape
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+				
+				// There is no intersection with the shadow path's shape.
+				int inc = 0;
+				if (data.hasX4ymin && 
+					data.x4ymin>=data.xmin4ymin) {
+					++inc;
+				}
+				if (data.hasX4ymax && 
+					data.x4ymax>=data.xmin4ymax) {
+					++inc;
+				}
+
+				if (y0<y1) {
+					numCrosses += inc;
+				}
+				else {
+					numCrosses -= inc;
+				}
+				
+				// Apply the previously computed crossings
+				numCrosses += crossings;
+			}
+
+			return numCrosses;
+		}
+
+		private static void computeCrossings1(
+				PathIterator2f pi, 
+				float x1, float y1, float x2, float y2, 
+				boolean closeable, PathShadowData data) {	
+			if (!pi.hasNext() || data.crossings==MathConstants.SHAPE_INTERSECTS) return;
+			PathElement2f element;
+
+			element = pi.next();
+			if (element.type != PathElementType.MOVE_TO) {
+				throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+			}
+
+			float movx = element.toX;
+			float movy = element.toY;
+			float curx = movx;
+			float cury = movy;
+			float endx, endy;
+			while (data.crossings!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+				element = pi.next();
+				switch (element.type) {
+				case MOVE_TO:
+					movx = curx = element.toX;
+					movy = cury = element.toY;
+					break;
+				case LINE_TO:
+					endx = element.toX;
+					endy = element.toY;
+					computeCrossings2(
+							curx, cury,
+							endx, endy,
+							x1, y1, x2, y2,
+							data);
+					if (data.crossings==MathConstants.SHAPE_INTERSECTS) {
+						return;
+					}
+					curx = endx;
+					cury = endy;
+					break;
+				case QUAD_TO:
+				{
+					endx = element.toX;
+					endy = element.toY;
+					Path2f localPath = new Path2f();
+					localPath.moveTo(curx, cury);
+					localPath.quadTo(
+							element.ctrlX1, element.ctrlY1,
+							endx, endy);
+					computeCrossings1(
+							localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+							x1, y1, x2, y2,
+							false,
+							data);
+					if (data.crossings==MathConstants.SHAPE_INTERSECTS) {
+						return;
+					}
+					curx = endx;
+					cury = endy;
+					break;
+				}
+				case CURVE_TO:
+					endx = element.toX;
+					endy = element.toY;
+					Path2f localPath = new Path2f();
+					localPath.moveTo(curx, cury);
+					localPath.curveTo(
+							element.ctrlX1, element.ctrlY1,
+							element.ctrlX2, element.ctrlY2,
+							endx, endy);
+					computeCrossings1(
+							localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+							x1, y1, x2, y2,
+							false,
+							data);
+					if (data.crossings==MathConstants.SHAPE_INTERSECTS) {
+						return;
+					}
+					curx = endx;
+					cury = endy;
+					break;
+				case CLOSE:
+					if (cury != movy || curx != movx) {
+						computeCrossings2(
+								curx, cury,
+								movx, movy,
+								x1, y1, x2, y2,
+								data);
+					}
+					if (data.crossings!=0)	return;
+					curx = movx;
+					cury = movy;
+					break;
+				default:
+				}
+			}
+
+			assert(data.crossings!=MathConstants.SHAPE_INTERSECTS);
+
+			boolean isOpen = (curx != movx) || (cury != movy);
+
+			if (isOpen) {
+				if (closeable) {
+					computeCrossings2(
+							curx, cury,
+							movx, movy,
+							x1, y1, x2, y2,
+							data);
+				}
+				else {
+					// Assume that when is the path is open, only
+					// SHAPE_INTERSECTS may be return
+					data.crossings = 0;
+				}
+			}
+		}
+
+		private static void computeCrossings2(
+				float shadow_x0, float shadow_y0,
+				float shadow_x1, float shadow_y1,
+				float sx0, float sy0,
+				float sx1, float sy1,
+				PathShadowData data) {
+			float shadow_xmin = Math.min(shadow_x0, shadow_x1);
+			float shadow_xmax = Math.max(shadow_x0, shadow_x1);
+			float shadow_ymin = Math.min(shadow_y0, shadow_y1);
+			float shadow_ymax = Math.max(shadow_y0, shadow_y1);
+			
+			data.updateShadowLimits(shadow_x0, shadow_y0, shadow_x1, shadow_y1);
+
+			if (sy0<=shadow_ymin && sy1<=shadow_ymin) return;
+			if (sy0>=shadow_ymax && sy1>=shadow_ymax) return;
+			if (sx0<=shadow_xmin && sx1<=shadow_xmin) return;
+			if (sx0>=shadow_xmax && sx1>=shadow_xmax) {
+				float xintercept;
+				// The line is entirely at the right of the shadow
+				float alpha = (sx1 - sx0) / (sy1 - sy0);
+				if (sy0<sy1) {
+					if (sy0<=shadow_ymin) {
+						xintercept = sx0 + (shadow_ymin - sy0) * alpha;
+						data.setCrossingForYMin(xintercept, shadow_ymin);
+						++data.crossings;
+					}
+					if (sy1>=shadow_ymax) {
+						xintercept = sx0 + (shadow_ymax - sy0) * alpha;
+						data.setCrossingForYMax(xintercept, shadow_ymax);
+						++data.crossings;
+					}
+				}
+				else {
+					if (sy1<=shadow_ymin) {
+						xintercept = sx0 + (shadow_ymin - sy0) * alpha;
+						data.setCrossingForYMin(xintercept, shadow_ymin);
+						--data.crossings;
+					}
+					if (sy0>=shadow_ymax) {
+						xintercept = sx0 + (shadow_ymax - sy0) * alpha;
+						data.setCrossingForYMax(xintercept, shadow_ymax);
+						--data.crossings;
+					}
+				}
+			}
+			else if (Segment2f.intersectsSegmentSegmentWithoutEnds(
+					shadow_x0, shadow_y0, shadow_x1, shadow_y1,
+					sx0, sy0, sx1, sy1)) {
+				data.crossings = MathConstants.SHAPE_INTERSECTS;
+			}
+			else {
+				int side1, side2;
+				boolean isUp = (shadow_y0<=shadow_y1);
+				if (isUp) {
+					side1 = MathUtil.sidePointLine(
+							shadow_x0, shadow_y0,
+							shadow_x1, shadow_y1,
+							sx0, sy0, false);
+					side2 = MathUtil.sidePointLine(
+							shadow_x0, shadow_y0,
+							shadow_x1, shadow_y1,
+							sx1, sy1, false);
+				}
+				else {
+					side1 = MathUtil.sidePointLine(
+							shadow_x1, shadow_y1,
+							shadow_x0, shadow_y0,
+							sx0, sy0, false);
+					side2 = MathUtil.sidePointLine(
+							shadow_x1, shadow_y1,
+							shadow_x0, shadow_y0,
+							sx1, sy1, false);
+				}
+				if (side1>0 || side2>0) {
+					computeCrossings3(
+							shadow_x0, shadow_y0,
+							sx0, sy0, sx1, sy1,
+							data, isUp);
+					computeCrossings3(
+							shadow_x1, shadow_y1,
+							sx0, sy0, sx1, sy1,
+							data, !isUp);
+				}
+			}
+		}
+
+		private static void computeCrossings3(
+				float shadowx, float shadowy,
+				float sx0, float sy0,
+				float sx1, float sy1,
+				PathShadowData data,
+				boolean isUp) {
+			if (shadowy <  sy0 && shadowy <  sy1) return;
+			if (shadowy > sy0 && shadowy > sy1) return;
+			if (shadowx > sx0 && shadowx > sx1) return;
+			float xintercept = sx0 + (shadowy - sy0) * (sx1 - sx0) / (sy1 - sy0);
+			if (shadowx > xintercept) return;
+			if (isUp) {
+				data.setCrossingForYMax(xintercept, shadowy);
+			}
+			else {
+				data.setCrossingForYMin(xintercept, shadowy);
+			}
+			data.crossings += (sy0 < sy1) ? 1 : -1;
+		}
+
+	} // class PathShadow
+
+	/** 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class PathShadowData {
+
+		public int crossings = 0;
+		public boolean hasX4ymin = false;
+		public boolean hasX4ymax = false;
+		public float x4ymin;
+		public float x4ymax;
+		public float xmin4ymin;
+		public float xmin4ymax;
+		public float ymin;
+		public float ymax;
 		
+		@Override
+		public String toString() {
+			StringBuilder b = new StringBuilder();
+			b.append("SHADOW {\n\tlow: ( "); //$NON-NLS-1$
+			b.append(this.xmin4ymin);
+			b.append(" | "); //$NON-NLS-1$
+			b.append(this.ymin);
+			b.append(" )\n\thigh: ( "); //$NON-NLS-1$
+			b.append(this.xmin4ymax);
+			b.append(" | "); //$NON-NLS-1$
+			b.append(this.ymax);
+			b.append(")\n}\nCROSSINGS {\n\tcrossings="); //$NON-NLS-1$
+			b.append(this.crossings);
+			b.append("\n\tlow: "); //$NON-NLS-1$
+			if (this.hasX4ymin) {
+				b.append("( "); //$NON-NLS-1$
+				b.append(this.x4ymin);
+				b.append(" | "); //$NON-NLS-1$
+				b.append(this.ymin);
+				b.append(" )\n"); //$NON-NLS-1$
+			}
+			else {
+				b.append("none\n"); //$NON-NLS-1$
+			}
+			b.append("\thigh: "); //$NON-NLS-1$
+			if (this.hasX4ymax) {
+				b.append("( "); //$NON-NLS-1$
+				b.append(this.x4ymax);
+				b.append(" | "); //$NON-NLS-1$
+				b.append(this.ymax);
+				b.append(" )\n"); //$NON-NLS-1$
+			}
+			else {
+				b.append("none\n"); //$NON-NLS-1$
+			}
+			b.append("}\n"); //$NON-NLS-1$
+			return b.toString();
+		}
+		
+		public PathShadowData(float xmax, float miny, float maxy) {
+			this.x4ymin = this.x4ymax = xmax;
+			this.xmin4ymax = this.xmin4ymin = xmax;
+			this.ymin = miny;
+			this.ymax = maxy;
+		}
+		
+		public void setCrossingForYMax(float x, float y) {
+			if (y>=this.ymax) {
+				if (x<this.x4ymax) {
+					this.x4ymax = x;
+					this.hasX4ymax = true;
+				}
+			}
+		}
+		
+		public void setCrossingForYMin(float x, float y) {
+			if (y<=this.ymin) {
+				if (x<this.x4ymin) {
+					this.x4ymin = x;
+					this.hasX4ymin = true;
+				}
+			}
+		}
+
+		public void updateShadowLimits(float shadow_x0, float shadow_y0, float shadow_x1, float shadow_y1) {
+			float xl, yl;
+			float xh, yh;
+			if (shadow_y0<shadow_y1) {
+				xl = shadow_x0;
+				yl = shadow_y0;
+				xh = shadow_x1;
+				yh = shadow_y1;
+			}
+			else if (shadow_y1<shadow_y0) {
+				xl = shadow_x1;
+				yl = shadow_y1;
+				xh = shadow_x0;
+				yh = shadow_y0;
+			}
+			else {
+				xl = xh = Math.min(shadow_x0, shadow_x1);
+				yl = yh = shadow_y0;
+			}
+
+			if (yl<=this.ymin && xl<this.xmin4ymin) {
+				this.xmin4ymin = xl;
+			}
+			
+			if (yh>=this.ymax && xh<this.xmin4ymax) {
+				this.xmin4ymax = xh;
+			}
+		}
+
 	}
 	
 }
\ No newline at end of file

Modified: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java	2013-04-08 09:07:54 UTC (rev 417)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -945,7 +945,7 @@
 
 	@Override
 	public boolean intersects(Path2f s) {
-		return intersects(s.getPathIterator());
+		return intersects(s.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO));
 	}
 
 	@Override

Modified: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractPath2fTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractPath2fTestCase.java	2013-04-08 09:07:54 UTC (rev 417)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractPath2fTestCase.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -134,6 +134,18 @@
 
 	/**
 	 */
+	public abstract void testContainsPathIterator2fFloatFloatFloatFloat();
+
+	/**
+	 */
+	public abstract void testIntersectsPath2f();
+
+	/**
+	 */
+	public abstract void testIntersectsPathIterator2f();
+	
+	/**
+	 */
 	public abstract void testIntersectsPathIterator2fFloatFloatFloatFloat();
 	
 	/**
@@ -197,6 +209,8 @@
 	@Override
 	public abstract void testToBoundingBox();
 	
+	public abstract void testToBoundingBoxWithCtrlPoints();
+
 	/**
 	 */
 	public abstract void testRemoveLast();

Modified: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/ClosePath2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/ClosePath2fTest.java	2013-04-08 09:07:54 UTC (rev 417)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/ClosePath2fTest.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -33,7 +33,72 @@
  */
 public class ClosePath2fTest extends AbstractPath2fTestCase {
 	
+	private Path2f r2;
+	private Path2f r3;
+	private Path2f r4;
+	private Path2f r5;
+	private Path2f r6;
+	private Path2f r7;
+	private Path2f r8;
+		
 	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		this.r2 = new Path2f();
+		this.r2.moveTo(2f, 1f);
+		this.r2.lineTo(4f, 0f);
+		this.r2.quadTo(5f, -2f, 2f, -3f);
+		this.r2.closePath();
+		
+		this.r3 = new Path2f();
+		this.r3.moveTo(5f, 0f);
+		this.r3.lineTo(6f, 2f);
+		this.r3.lineTo(7f, 1f);
+		this.r3.closePath();
+
+		this.r4 = new Path2f();
+		this.r4.moveTo(1f, 2f);
+		this.r4.lineTo(0f, 1f);
+		this.r4.lineTo(3f, 0f);
+
+		this.r5 = new Path2f();
+		this.r5.moveTo(3f, 0f);
+		this.r5.lineTo(4f, 1f);
+		this.r5.lineTo(5f, -2f);
+		this.r5.closePath();
+
+		this.r6 = new Path2f();
+		this.r6.moveTo(4f, 4f);
+		this.r6.lineTo(0f, 2f);
+		this.r6.lineTo(2f, -3f);
+		this.r6.lineTo(7f, -6f);
+		this.r6.lineTo(8f, 0f);
+
+		this.r7 = new Path2f();
+		this.r7.moveTo(4f, 4f);
+		this.r7.lineTo(0f, 2f);
+		this.r7.lineTo(2f, -3f);
+		this.r7.lineTo(7f, -6f);
+		this.r7.lineTo(8f, 0f);
+		this.r7.closePath();
+
+		this.r8 = new Path2f();
+		this.r8.moveTo(0f, -5f);
+		this.r8.lineTo(1f, -3f);
+		this.r8.lineTo(2f, -6f);
+		this.r8.closePath();
+	}
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r2 = this.r3 = this.r4 = null;
+		this.r5 = this.r6 = this.r7 = null;
+		this.r8 = null;
+		super.tearDown();
+	}
+
+	@Override
 	protected Path2f createShape() {
 		Path2f p = new Path2f();
 		p.moveTo(1f, 1f);
@@ -294,6 +359,24 @@
 		assertFalse(this.r.contains(new Rectangle2f(.01f, .01f, 1f, 1f)));
 	}
 
+	/**
+	 */
+	@Override
+	public void testContainsPathIterator2fFloatFloatFloatFloat() {
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 0f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, 3f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 2f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 1f, 1f, 1f));
+		assertTrue(Path2f.contains(this.r.getPathIterator(), 3f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), -1f, -1f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, -3f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), -3f, 4f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 6f, -5f, 1f, 1f));
+		assertTrue(Path2f.contains(this.r.getPathIterator(), 4f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 5f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), .01f, .01f, 1f, 1f));
+	}
+
 	/** 
 	 */
 	@Override
@@ -327,7 +410,45 @@
 		assertTrue(Path2f.intersects(this.r.getPathIterator(), 5f, 0f, 1f, 1f));
 		assertTrue(Path2f.intersects(this.r.getPathIterator(), .01f, .01f, 1f, 1f));
 	}
+	
+	@Override
+	public void testIntersectsPath2f() {
+		assertTrue(this.r.intersects(this.r2));
+		assertTrue(this.r.intersects(this.r3));
+		assertTrue(this.r.intersects(this.r4));
+		assertTrue(this.r.intersects(this.r5));
+		assertFalse(this.r.intersects(this.r6));
+		assertTrue(this.r.intersects(this.r7));
+		assertFalse(this.r.intersects(this.r8));
 
+		assertTrue(this.r2.intersects(this.r));
+		assertTrue(this.r3.intersects(this.r));
+		assertTrue(this.r4.intersects(this.r));
+		assertTrue(this.r5.intersects(this.r));
+		assertFalse(this.r6.intersects(this.r));
+		assertTrue(this.r7.intersects(this.r));
+		assertFalse(this.r8.intersects(this.r));
+	}
+
+	@Override
+	public void testIntersectsPathIterator2f() {
+		assertTrue(this.r.intersects(this.r2.getPathIterator()));
+		assertTrue(this.r.intersects(this.r3.getPathIterator()));
+		assertTrue(this.r.intersects(this.r4.getPathIterator()));
+		assertTrue(this.r.intersects(this.r5.getPathIterator()));
+		assertFalse(this.r.intersects(this.r6.getPathIterator()));
+		assertTrue(this.r.intersects(this.r7.getPathIterator()));
+		assertFalse(this.r.intersects(this.r8.getPathIterator()));
+
+		assertTrue(this.r2.intersects(this.r.getPathIterator()));
+		assertTrue(this.r3.intersects(this.r.getPathIterator()));
+		assertTrue(this.r4.intersects(this.r.getPathIterator()));
+		assertTrue(this.r5.intersects(this.r.getPathIterator()));
+		assertFalse(this.r6.intersects(this.r.getPathIterator()));
+		assertTrue(this.r7.intersects(this.r.getPathIterator()));
+		assertFalse(this.r8.intersects(this.r.getPathIterator()));
+	}
+
 	/** 
 	 */
 	@Override
@@ -393,6 +514,15 @@
 		assertEpsilonEquals(1f, bb.getMinX());
 		assertEpsilonEquals(-5f, bb.getMinY());
 		assertEpsilonEquals(7f, bb.getMaxX());
+		assertEpsilonEquals(3f, bb.getMaxY());
+	}
+	
+	@Override
+	public void testToBoundingBoxWithCtrlPoints() {
+		Rectangle2f bb = this.r.toBoundingBoxWithCtrlPoints();
+		assertEpsilonEquals(1f, bb.getMinX());
+		assertEpsilonEquals(-5f, bb.getMinY());
+		assertEpsilonEquals(7f, bb.getMaxX());
 		assertEpsilonEquals(5f, bb.getMaxY());
 	}
 	

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/GeneratePictureForTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/GeneratePictureForTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/GeneratePictureForTest.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -0,0 +1,379 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import junit.framework.TestCase;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.vmutil.FileSystem;
+
+/**
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class GeneratePictureForTest extends TestCase {
+
+	private static final boolean GENERATE_TESTING_PICTURE = false;
+	
+	private Path2f r1;
+	private Path2f r2;
+	private Path2f r3;
+	private Path2f r4;
+	private Path2f r5;
+	private Path2f r6;
+	private Path2f r7;
+	private Path2f r8;
+		
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		this.r1 = new Path2f();
+		this.r1.moveTo(1f, 1f);
+		this.r1.lineTo(2f, 2f);
+		this.r1.quadTo(3f, 0f, 4f, 3f);
+		this.r1.curveTo(5f, -1f, 6f, 5f, 7f, -5f);
+
+		this.r2 = new Path2f();
+		this.r2.moveTo(2f, 1f);
+		this.r2.lineTo(4f, 0f);
+		this.r2.quadTo(5f, -2f, 2f, -3f);
+		this.r2.closePath();
+		
+		this.r3 = new Path2f();
+		this.r3.moveTo(5f, 0f);
+		this.r3.lineTo(6f, 2f);
+		this.r3.lineTo(7f, 1f);
+		this.r3.closePath();
+
+		this.r4 = new Path2f();
+		this.r4.moveTo(1f, 2f);
+		this.r4.lineTo(0f, 1f);
+		this.r4.lineTo(3f, 0f);
+
+		this.r5 = new Path2f();
+		this.r5.moveTo(3f, 0f);
+		this.r5.lineTo(4f, 1f);
+		this.r5.lineTo(5f, -2f);
+		this.r5.closePath();
+
+		this.r6 = new Path2f();
+		this.r6.moveTo(4f, 4f);
+		this.r6.lineTo(0f, 2f);
+		this.r6.lineTo(2f, -3f);
+		this.r6.lineTo(7f, -6f);
+		this.r6.lineTo(8f, 0f);
+
+		this.r7 = new Path2f();
+		this.r7.moveTo(4f, 4f);
+		this.r7.lineTo(0f, 2f);
+		this.r7.lineTo(2f, -3f);
+		this.r7.lineTo(7f, -6f);
+		this.r7.lineTo(8f, 0f);
+		this.r7.closePath();
+
+		this.r8 = new Path2f();
+		this.r8.moveTo(0f, -5f);
+		this.r8.lineTo(1f, -3f);
+		this.r8.lineTo(2f, -6f);
+		this.r8.closePath();
+	}
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r2 = this.r3 = this.r4 = null;
+		this.r5 = this.r6 = this.r7 = null;
+		this.r8 = null;
+		super.tearDown();
+	}
+
+	private static int toX(float x) {
+		return (int)(x * 100) + 20;
+	}
+
+	private static int toY(float y) {
+		return (int)(1000 - ((y * 100) + 600));
+	}
+
+	private static int toS(float s) {
+		return (int)(s * 100);
+	}
+
+	public void testImageGeneration() throws IOException {
+		PathIterator2f pi;
+		BufferedImage img = new BufferedImage(800, 1000, BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = (Graphics2D)img.getGraphics();
+		//Background
+		g.setColor(Color.WHITE);
+		g.fillRect(0, 0, 800, 1000);
+
+		//
+		// PATHS
+		Path2f[] paths = new Path2f[] {
+				this.r1,
+				this.r2,
+				this.r3,
+				this.r4,
+				this.r5,
+				this.r6,
+				this.r7,
+				this.r8,
+		};
+
+		//
+		// POINTS
+		Point2f[] points = new Point2f[] {
+				//				new Point2f(0f, 0f),
+				//				new Point2f(4f, 3f),
+				//				new Point2f(2f, 2f),
+				//				new Point2f(2f, 1f),
+				//				new Point2f(5f, 0f),
+				//				new Point2f(-1f, -1f),
+				//				new Point2f(5f, 2f),
+				//				new Point2f(3.5f, -2.5f),
+				//				new Point2f(7f, -4f),
+				//				new Point2f(2.5f, 1.5f),
+		};
+
+		//
+		// LINES
+		Segment2f[] lines = new Segment2f[] {
+				//new Segment2f(6.7773438f, -3.0272121f, 6.7890625f, -3.1188917f),
+				//new Segment2f(6.7890625f, -3.1188917f, 6.8007812f, -3.2118688f),
+				//new Segment2f(6.8007812f, -3.2118688f, 6.8125f, -3.3061523f),
+				//new Segment2f(6.9414062f, -4.4321795f, 6.953125f, -4.5428696f),
+				//new Segment2f(.5f, -1f, .5f, 2f),
+		};
+
+		//
+		// SEGMENTS
+		Segment2f[] segments = new Segment2f[] {
+				//				new Segment2f(0f, 0f, 1f, 1f),
+				//				new Segment2f(4f, 3f, 1f, 1f),
+				//				new Segment2f(2f, 2f, 1f, 1f),
+				//				new Segment2f(2f, 1f, 1f, 1f),
+				//				new Segment2f(3f, 0f, 1f, 1f),
+				//				new Segment2f(3f, 0f, 1.5f, 1f),
+				//				new Segment2f(-1f, -1f, 1f, 1f),
+				//				new Segment2f(4f, -3f, 1f, 1f),
+				//				new Segment2f(4f, -3f, 1f, 0f),
+				//				new Segment2f(-3f, 4f, 1f, 1f),
+				//				new Segment2f(6f, -5f, 1f, 1f),
+				//				new Segment2f(6f, -5f, 1f, 0f),
+				//				new Segment2f(4f, 0f, 1f, 1f),
+				//				new Segment2f(5f, 0f, 1f, 1f),
+				//				new Segment2f(.01f, .01f, 1f, 1f),
+		};
+
+		//
+		// CIRCLES
+		Circle2f[] circles = new Circle2f[] {
+				//				new Circle2f(0f, 0f, 1f),
+				//				new Circle2f(4f, 3f, 1f),
+				//				new Circle2f(2f, 2f, 1f),
+				//				new Circle2f(2f, 1f, 1f),
+				//				new Circle2f(3f, 0f, 1f),
+				//				new Circle2f(-1f, -1f, 1f),
+				//				new Circle2f(4f, -3f, 1f),
+				//				new Circle2f(-3f, 4f, 1f),
+				//				new Circle2f(6f, -5f, 1f),
+				//				new Circle2f(6f, -5f, .95f),
+				//				new Circle2f(4f, 0f, 1f),
+				//				new Circle2f(5f, 0f, 1f),
+				//				new Circle2f(.01f, .01f, 1f),
+				//				new Circle2f(6f, 2f, .8f),
+		};
+
+		//
+		// RECTANGLES
+		Rectangle2f[] rectangles = new Rectangle2f[] {
+//				new Rectangle2f(0f, 0f, 1f, 1f),
+//				new Rectangle2f(4f, 3f, 1f, 1f),
+//				new Rectangle2f(2f, 2f, 1f, 1f),
+//				new Rectangle2f(2f, 1f, 1f, 1f),
+//				new Rectangle2f(3f, 0f, 1f, 1f),
+//				new Rectangle2f(-1f, -1f, 1f, 1f),
+//				new Rectangle2f(4f, -3f, 1f, 1f),
+//				new Rectangle2f(-3f, 4f, 1f, 1f),
+//				new Rectangle2f(6f, -5f, 1f, 1f),
+//				new Rectangle2f(4f, 0f, 1f, 1f),
+//				new Rectangle2f(5f, 0f, 1f, 1f),
+//				new Rectangle2f(.01f, .01f, 1f, 1f),
+		};
+
+		//
+		// ELLIPSES
+		Ellipse2f[] ellipses = new Ellipse2f[] {
+				//				new Ellipse2f(0f, 0f, 1f, 2f),
+				//				new Ellipse2f(4f, 3f, 1f, 2f),
+				//				new Ellipse2f(2f, 2f, 1f, 2f),
+				//				new Ellipse2f(2f, 1f, 1f, 2f),
+				//				new Ellipse2f(3f, 0f, 1f, 2f),
+				//				new Ellipse2f(-1f, -1f, 1f, 2f),
+				//				new Ellipse2f(4f, -3f, 1f, 2f),
+				//				new Ellipse2f(-3f, 4f, 1f, 2f),
+				//				new Ellipse2f(6f, -5f, 1f, 2f),
+				//				new Ellipse2f(6f, -5f, .8f, 2f),
+				//				new Ellipse2f(4f, 0f, 1f, 2f),
+				//				new Ellipse2f(6f, 0f, 1f, 2f),
+		};
+
+		// Elements
+		g.setColor(Color.ORANGE);
+		for(Point2f obj : points) {
+			g.fillRect(
+					toX(obj.getX()) - 4, toY(obj.getY()) - 4,
+					9, 9);
+		}
+		Stroke oldStroke = g.getStroke();
+		for(Segment2f obj : lines) {
+			int xx1 = toX(obj.getX1());
+			int yy1 = toY(obj.getY1());
+			int xx2 = toX(obj.getX2());
+			int yy2 = toY(obj.getY2());
+
+			g.setStroke(new BasicStroke(2));
+			Vector2f v = new Vector2f(xx2 - xx1, yy2 - yy1);
+			v.normalize();
+			v.scale(1000);
+			int px1 = (int)(xx1 + v.x);
+			int py1 = (int)(yy1 + v.y);
+			int px2 = (int)(xx1 - v.x);
+			int py2 = (int)(yy1 - v.y);
+			g.drawLine(px1, py1, px2, py2);
+
+			g.setStroke(new BasicStroke(4));
+			g.drawLine(xx1, yy1, xx2, yy2);
+		}
+		g.setStroke(new BasicStroke(3));
+		for(Segment2f obj : segments) {
+			g.drawLine(
+					toX(obj.getX1()), toY(obj.getY1()),
+					toX(obj.getX2()), toY(obj.getY2()));
+		}
+		g.setStroke(oldStroke);
+		for(Circle2f obj : circles) {
+			int r = toS(obj.getRadius());
+			g.drawOval(
+					toX(obj.getX())-r, toY(obj.getY())-r,
+					r*2, r*2);
+		}
+		for(Ellipse2f obj : ellipses) {
+			int x1 = toX(obj.getMinX());
+			int y1 = toY(obj.getMinY());
+			int x2 = toX(obj.getMaxX());
+			int y2 = toY(obj.getMaxY());
+			g.drawOval(
+					Math.min(x1, x2), Math.min(y1, y2),
+					toS(obj.getWidth()), toS(obj.getHeight()));
+		}
+		for(Rectangle2f obj : rectangles) {
+			int x1 = toX(obj.getMinX());
+			int y1 = toY(obj.getMinY());
+			int x2 = toX(obj.getMaxX());
+			int y2 = toY(obj.getMaxY());
+			g.fillRect(
+					Math.min(x1, x2), Math.min(y1, y2),
+					toS(obj.getWidth()), toS(obj.getHeight()));
+		}
+
+		// Cells
+		g.setColor(Color.LIGHT_GRAY);
+		for(float y=-6; y<=4; y+=1) {
+			if (y!=0f) g.drawLine(toX(-10), toY(y), toX(10), toY(y));
+		}
+		for(float x=-1; x<=8; x+=1) {
+			if (x!=0f) g.drawLine(toX(x), toY(-10), toX(x), toY(10));
+		}
+		g.setColor(Color.BLACK);
+		g.drawLine(toX(-10), toY(0), toX(10), toY(0));
+		g.drawLine(toX(0), toY(-10), toX(0), toY(10));
+
+		// Path
+		Color[] colors = new Color[] {
+			Color.RED, Color.BLUE, Color.CYAN, Color.GREEN
+		};
+		int colorIndex = 0;
+		for(Path2f path : paths) {
+			g.setColor(colors[colorIndex]);
+			colorIndex = (colorIndex+1)%colors.length;
+			pi = path.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+			boolean closed = false;
+			int mx, my, cx, cy;
+			mx = my = cx = cy = 0;
+			while (pi.hasNext()) {
+				PathElement2f pe = pi.next();
+				switch(pe.type) {
+				case MOVE_TO:
+					cx = mx = toX(pe.toX);
+					cy = my = toY(pe.toY);
+					break;
+				case LINE_TO:
+					cx = toX(pe.toX);
+					cy = toY(pe.toY);
+					g.drawLine(
+							toX(pe.fromX), toY(pe.fromY),
+							cx, cy);
+					break;
+				case CLOSE:
+					cx = toX(pe.toX);
+					cy = toY(pe.toY);
+					g.drawLine(
+							toX(pe.fromX), toY(pe.fromY),
+							cx, cy);
+					closed = true;
+					break;
+				default:
+				}
+			}
+			if (!closed) {
+				g.setStroke(new BasicStroke(
+						1,
+						BasicStroke.CAP_ROUND,
+						BasicStroke.JOIN_ROUND,
+						2,
+						new float[]{5,5},
+						0f));
+				g.drawLine(
+						mx, my,
+						cx, cy);
+				g.setStroke(oldStroke);
+			}
+		}
+		g.dispose();
+		if (GENERATE_TESTING_PICTURE)
+			ImageIO.write(img, "png", new File(FileSystem.getUserHomeDirectory(), "mytest.png"));  //$NON-NLS-1$//$NON-NLS-2$
+	}
+	
+}
\ No newline at end of file

Modified: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/OpenPath2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/OpenPath2fTest.java	2013-04-08 09:07:54 UTC (rev 417)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/OpenPath2fTest.java	2013-04-08 09:09:17 UTC (rev 418)
@@ -33,7 +33,72 @@
  */
 public class OpenPath2fTest extends AbstractPath2fTestCase {
 
+	private Path2f r2;
+	private Path2f r3;
+	private Path2f r4;
+	private Path2f r5;
+	private Path2f r6;
+	private Path2f r7;
+	private Path2f r8;
+		
 	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		this.r2 = new Path2f();
+		this.r2.moveTo(2f, 1f);
+		this.r2.lineTo(4f, 0f);
+		this.r2.quadTo(5f, -2f, 2f, -3f);
+		this.r2.closePath();
+		
+		this.r3 = new Path2f();
+		this.r3.moveTo(5f, 0f);
+		this.r3.lineTo(6f, 2f);
+		this.r3.lineTo(7f, 1f);
+		this.r3.closePath();
+
+		this.r4 = new Path2f();
+		this.r4.moveTo(1f, 2f);
+		this.r4.lineTo(0f, 1f);
+		this.r4.lineTo(3f, 0f);
+
+		this.r5 = new Path2f();
+		this.r5.moveTo(3f, 0f);
+		this.r5.lineTo(4f, 1f);
+		this.r5.lineTo(5f, -2f);
+		this.r5.closePath();
+
+		this.r6 = new Path2f();
+		this.r6.moveTo(4f, 4f);
+		this.r6.lineTo(0f, 2f);
+		this.r6.lineTo(2f, -3f);
+		this.r6.lineTo(7f, -6f);
+		this.r6.lineTo(8f, 0f);
+
+		this.r7 = new Path2f();
+		this.r7.moveTo(4f, 4f);
+		this.r7.lineTo(0f, 2f);
+		this.r7.lineTo(2f, -3f);
+		this.r7.lineTo(7f, -6f);
+		this.r7.lineTo(8f, 0f);
+		this.r7.closePath();
+
+		this.r8 = new Path2f();
+		this.r8.moveTo(0f, -5f);
+		this.r8.lineTo(1f, -3f);
+		this.r8.lineTo(2f, -6f);
+		this.r8.closePath();
+	}
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r2 = this.r3 = this.r4 = null;
+		this.r5 = this.r6 = this.r7 = null;
+		this.r8 = null;
+		super.tearDown();
+	}
+
+	@Override
 	protected Path2f createShape() {
 		Path2f p = new Path2f();
 		p.moveTo(1f, 1f);
@@ -238,242 +303,6 @@
 		assertNoElement(pi);
 	}
 
-/*	private static int toX(float x) {
-		return (int)(x * 100) + 20;
-	}
-
-	private static int toY(float y) {
-		return (int)(1000 - ((y * 100) + 600));
-	}
-
-	private static int toS(float s) {
-		return (int)(s * 100);
-	}
-
-	public void testImageGeneration() throws IOException {
-		PathIterator2f pi;
-		BufferedImage img = new BufferedImage(800, 1000, BufferedImage.TYPE_INT_ARGB);
-		Graphics2D g = (Graphics2D)img.getGraphics();
-		//Background
-		g.setColor(Color.WHITE);
-		g.fillRect(0, 0, 800, 1000);
-
-		//
-		// POINTS
-		Point2f[] points = new Point2f[] {
-				//				new Point2f(0f, 0f),
-				//				new Point2f(4f, 3f),
-				//				new Point2f(2f, 2f),
-				//				new Point2f(2f, 1f),
-				//				new Point2f(5f, 0f),
-				//				new Point2f(-1f, -1f),
-				//				new Point2f(5f, 2f),
-				//				new Point2f(3.5f, -2.5f),
-				//				new Point2f(7f, -4f),
-				//				new Point2f(2.5f, 1.5f),
-		};
-
-		//
-		// LINES
-		Segment2f[] lines = new Segment2f[] {
-				//new Segment2f(6.7773438f, -3.0272121f, 6.7890625f, -3.1188917f),
-				//new Segment2f(6.7890625f, -3.1188917f, 6.8007812f, -3.2118688f),
-				//new Segment2f(6.8007812f, -3.2118688f, 6.8125f, -3.3061523f),
-				//new Segment2f(6.9414062f, -4.4321795f, 6.953125f, -4.5428696f),
-				//new Segment2f(.5f, -1f, .5f, 2f),
-		};
-
-		//
-		// SEGMENTS
-		Segment2f[] segments = new Segment2f[] {
-				//				new Segment2f(0f, 0f, 1f, 1f),
-				//				new Segment2f(4f, 3f, 1f, 1f),
-				//				new Segment2f(2f, 2f, 1f, 1f),
-				//				new Segment2f(2f, 1f, 1f, 1f),
-				//				new Segment2f(3f, 0f, 1f, 1f),
-				//				new Segment2f(3f, 0f, 1.5f, 1f),
-				//				new Segment2f(-1f, -1f, 1f, 1f),
-				//				new Segment2f(4f, -3f, 1f, 1f),
-				//				new Segment2f(4f, -3f, 1f, 0f),
-				//				new Segment2f(-3f, 4f, 1f, 1f),
-				//				new Segment2f(6f, -5f, 1f, 1f),
-				//				new Segment2f(6f, -5f, 1f, 0f),
-				//				new Segment2f(4f, 0f, 1f, 1f),
-				//				new Segment2f(5f, 0f, 1f, 1f),
-				//				new Segment2f(.01f, .01f, 1f, 1f),
-		};
-
-		//
-		// CIRCLES
-		Circle2f[] circles = new Circle2f[] {
-				//				new Circle2f(0f, 0f, 1f),
-				//				new Circle2f(4f, 3f, 1f),
-				//				new Circle2f(2f, 2f, 1f),
-				//				new Circle2f(2f, 1f, 1f),
-				//				new Circle2f(3f, 0f, 1f),
-				//				new Circle2f(-1f, -1f, 1f),
-				//				new Circle2f(4f, -3f, 1f),
-				//				new Circle2f(-3f, 4f, 1f),
-				//				new Circle2f(6f, -5f, 1f),
-				//				new Circle2f(6f, -5f, .95f),
-				//				new Circle2f(4f, 0f, 1f),
-				//				new Circle2f(5f, 0f, 1f),
-				//				new Circle2f(.01f, .01f, 1f),
-				//				new Circle2f(6f, 2f, .8f),
-		};
-
-		//
-		// RECTANGLES
-		Rectangle2f[] rectangles = new Rectangle2f[] {
-				new Rectangle2f(0f, 0f, 1f, 1f),
-				new Rectangle2f(4f, 3f, 1f, 1f),
-				new Rectangle2f(2f, 2f, 1f, 1f),
-				new Rectangle2f(2f, 1f, 1f, 1f),
-				new Rectangle2f(3f, 0f, 1f, 1f),
-				new Rectangle2f(-1f, -1f, 1f, 1f),
-				new Rectangle2f(4f, -3f, 1f, 1f),
-				new Rectangle2f(-3f, 4f, 1f, 1f),
-				new Rectangle2f(6f, -5f, 1f, 1f),
-				new Rectangle2f(4f, 0f, 1f, 1f),
-				new Rectangle2f(5f, 0f, 1f, 1f),
-				new Rectangle2f(.01f, .01f, 1f, 1f),
-		};
-
-		//
-		// ELLIPSES
-		Ellipse2f[] ellipses = new Ellipse2f[] {
-				//				new Ellipse2f(0f, 0f, 1f, 2f),
-				//				new Ellipse2f(4f, 3f, 1f, 2f),
-				//				new Ellipse2f(2f, 2f, 1f, 2f),
-				//				new Ellipse2f(2f, 1f, 1f, 2f),
-				//				new Ellipse2f(3f, 0f, 1f, 2f),
-				//				new Ellipse2f(-1f, -1f, 1f, 2f),
-				//				new Ellipse2f(4f, -3f, 1f, 2f),
-				//				new Ellipse2f(-3f, 4f, 1f, 2f),
-				//				new Ellipse2f(6f, -5f, 1f, 2f),
-				//				new Ellipse2f(6f, -5f, .8f, 2f),
-				//				new Ellipse2f(4f, 0f, 1f, 2f),
-				//				new Ellipse2f(6f, 0f, 1f, 2f),
-		};
-
-		// Elements
-		g.setColor(Color.ORANGE);
-		for(Point2f obj : points) {
-			g.fillRect(
-					toX(obj.getX()) - 4, toY(obj.getY()) - 4,
-					9, 9);
-		}
-		Stroke oldStroke = g.getStroke();
-		for(Segment2f obj : lines) {
-			int xx1 = toX(obj.getX1());
-			int yy1 = toY(obj.getY1());
-			int xx2 = toX(obj.getX2());
-			int yy2 = toY(obj.getY2());
-
-			g.setStroke(new BasicStroke(2));
-			Vector2f v = new Vector2f(xx2 - xx1, yy2 - yy1);
-			v.normalize();
-			v.scale(1000);
-			int px1 = (int)(xx1 + v.x);
-			int py1 = (int)(yy1 + v.y);
-			int px2 = (int)(xx1 - v.x);
-			int py2 = (int)(yy1 - v.y);
-			g.drawLine(px1, py1, px2, py2);
-
-			g.setStroke(new BasicStroke(4));
-			g.drawLine(xx1, yy1, xx2, yy2);
-		}
-		g.setStroke(new BasicStroke(3));
-		for(Segment2f obj : segments) {
-			g.drawLine(
-					toX(obj.getX1()), toY(obj.getY1()),
-					toX(obj.getX2()), toY(obj.getY2()));
-		}
-		g.setStroke(oldStroke);
-		for(Circle2f obj : circles) {
-			int r = toS(obj.getRadius());
-			g.drawOval(
-					toX(obj.getX())-r, toY(obj.getY())-r,
-					r*2, r*2);
-		}
-		for(Ellipse2f obj : ellipses) {
-			int x1 = toX(obj.getMinX());
-			int y1 = toY(obj.getMinY());
-			int x2 = toX(obj.getMaxX());
-			int y2 = toY(obj.getMaxY());
-			g.drawOval(
-					Math.min(x1, x2), Math.min(y1, y2),
-					toS(obj.getWidth()), toS(obj.getHeight()));
-		}
-		for(Rectangle2f obj : rectangles) {
-			int x1 = toX(obj.getMinX());
-			int y1 = toY(obj.getMinY());
-			int x2 = toX(obj.getMaxX());
-			int y2 = toY(obj.getMaxY());
-			g.fillRect(
-					Math.min(x1, x2), Math.min(y1, y2),
-					toS(obj.getWidth()), toS(obj.getHeight()));
-		}
-
-		// Cells
-		g.setColor(Color.LIGHT_GRAY);
-		for(float y=-6; y<=4; y+=1) {
-			if (y!=0f) g.drawLine(toX(-10), toY(y), toX(10), toY(y));
-		}
-		for(float x=-1; x<=8; x+=1) {
-			if (x!=0f) g.drawLine(toX(x), toY(-10), toX(x), toY(10));
-		}
-		g.setColor(Color.BLACK);
-		g.drawLine(toX(-10), toY(0), toX(10), toY(0));
-		g.drawLine(toX(0), toY(-10), toX(0), toY(10));
-
-		// Path
-		g.setColor(Color.RED);
-		pi = this.r.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
-		boolean closed = false;
-		int mx, my, cx, cy;
-		mx = my = cx = cy = 0;
-		while (pi.hasNext()) {
-			PathElement2f pe = pi.next();
-			switch(pe.type) {
-			case MOVE_TO:
-				cx = mx = toX(pe.toX);
-				cy = my = toY(pe.toY);
-				break;
-			case LINE_TO:
-				cx = toX(pe.toX);
-				cy = toY(pe.toY);
-				g.drawLine(
-						toX(pe.fromX), toY(pe.fromY),
-						cx, cy);
-				break;
-			case CLOSE:
-				cx = toX(pe.toX);
-				cy = toY(pe.toY);
-				g.drawLine(
-						toX(pe.fromX), toY(pe.fromY),
-						cx, cy);
-				closed = true;
-				break;
-			default:
-			}
-		}
-		if (!closed) {
-			g.setStroke(new BasicStroke(
-					1,
-					BasicStroke.CAP_ROUND,
-					BasicStroke.JOIN_ROUND,
-					2,
-					new float[]{5,5},
-					0f));
-			g.drawLine(
-					mx, my,
-					cx, cy);
-		}
-		g.dispose();
-		ImageIO.write(img, "png", new File(FileSystem.getUserHomeDirectory(), "mytest.png"));
-	}*/
-
 	/**
 	 */
 	@Override
@@ -520,6 +349,23 @@
 		assertFalse(this.r.contains(new Rectangle2f(.01f, .01f, 1f, 1f)));
 	}
 
+	/**
+	 */
+	@Override
+	public void testContainsPathIterator2fFloatFloatFloatFloat() {
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 0f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, 3f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 2f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 1f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 3f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), -1f, -1f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, -3f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), -3f, 4f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 6f, -5f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 5f, 0f, 1f, 1f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), .01f, .01f, 1f, 1f));
+	}
 	/** 
 	 */
 	@Override
@@ -554,6 +400,44 @@
 		assertTrue(Path2f.intersects(this.r.getPathIterator(), .01f, .01f, 1f, 1f));
 	}
 
+	@Override
+	public void testIntersectsPath2f() {
+		assertFalse(this.r.intersects(this.r2));
+		assertTrue(this.r.intersects(this.r3));
+		assertFalse(this.r.intersects(this.r4));
+		assertFalse(this.r.intersects(this.r5));
+		assertFalse(this.r.intersects(this.r6));
+		assertTrue(this.r.intersects(this.r7));
+		assertFalse(this.r.intersects(this.r8));
+
+		assertFalse(this.r2.intersects(this.r));
+		assertTrue(this.r3.intersects(this.r));
+		assertFalse(this.r4.intersects(this.r));
+		assertFalse(this.r5.intersects(this.r));
+		assertFalse(this.r6.intersects(this.r));
+		assertTrue(this.r7.intersects(this.r));
+		assertFalse(this.r8.intersects(this.r));
+	}
+
+	@Override
+	public void testIntersectsPathIterator2f() {
+		assertFalse(this.r.intersects(this.r2.getPathIterator()));
+		assertTrue(this.r.intersects(this.r3.getPathIterator()));
+		assertFalse(this.r.intersects(this.r4.getPathIterator()));
+		assertFalse(this.r.intersects(this.r5.getPathIterator()));
+		assertFalse(this.r.intersects(this.r6.getPathIterator()));
+		assertTrue(this.r.intersects(this.r7.getPathIterator()));
+		assertFalse(this.r.intersects(this.r8.getPathIterator()));
+
+		assertFalse(this.r2.intersects(this.r.getPathIterator()));
+		assertTrue(this.r3.intersects(this.r.getPathIterator()));
+		assertFalse(this.r4.intersects(this.r.getPathIterator()));
+		assertFalse(this.r5.intersects(this.r.getPathIterator()));
+		assertFalse(this.r6.intersects(this.r.getPathIterator()));
+		assertTrue(this.r7.intersects(this.r.getPathIterator()));
+		assertFalse(this.r8.intersects(this.r.getPathIterator()));
+	}
+
 	/** 
 	 */
 	@Override
@@ -619,6 +503,15 @@
 		assertEpsilonEquals(1f, bb.getMinX());
 		assertEpsilonEquals(-5f, bb.getMinY());
 		assertEpsilonEquals(7f, bb.getMaxX());
+		assertEpsilonEquals(3f, bb.getMaxY());
+	}
+	
+	@Override
+	public void testToBoundingBoxWithCtrlPoints() {
+		Rectangle2f bb = this.r.toBoundingBoxWithCtrlPoints();
+		assertEpsilonEquals(1f, bb.getMinX());
+		assertEpsilonEquals(-5f, bb.getMinY());
+		assertEpsilonEquals(7f, bb.getMaxX());
 		assertEpsilonEquals(5f, bb.getMaxY());
 	}
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/