[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());
}