/*
* $Id$
*
* Copyright (C) 2015 Stephane GALLAND, Hamza JAFFALI.
*
* 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 3 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.geometry.d3.continuous;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.arakhne.afc.math.MathConstants;
import org.arakhne.afc.math.geometry.PathElementType;
import org.arakhne.afc.math.geometry.PathWindingRule;
import org.arakhne.afc.math.geometry.d3.FunctionalPoint3D;
import org.arakhne.afc.math.geometry.d3.Path3D;
import org.arakhne.afc.math.geometry.d3.Point3D;
import org.eclipse.xtext.xbase.lib.Pure;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
/** A generic 3D-path.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class Path3f extends AbstractShape3F<Path3f> implements Path3D<Shape3F,AlignedBox3f,AbstractPathElement3F,PathIterator3f> {
private static final long serialVersionUID = -8167977956565440101L;
/** Multiple of cubic & quad curve size.
*/
//36 = (3+3+3)*4
static final int GROW_SIZE = 36;
/** Replies the point on the path that is closest to the given point.
* <p>
* <strong>CAUTION:</strong> This function works only on path iterators
* that are replying polyline primitives, ie. if the
* {@link PathIterator3f#isPolyline()} of <var>pi</var> is replying
* <code>true</code>.
* {@link #getClosestPointTo(Point3D)} avoids this restriction.
*
* @param pi is the iterator on the elements of the path.
* @param x
* @param y
* @param z
* @return the closest point on the shape; or the point itself
* if it is inside the shape.
*/
public static Point3D getClosestPointTo(PathIterator3f pathIterator, double x, double y, double z) {
Point3D closest = null;
double bestDist = Double.POSITIVE_INFINITY;
Point3D candidate;
AbstractPathElement3F pe = pathIterator.next();
Path3f subPath;
if (pe.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
candidate = new Point3f(pe.getToX(), pe.getToY(), pe.getToZ());
while (pathIterator.hasNext()) {
pe = pathIterator.next();
candidate = null;
switch(pe.type) {
case MOVE_TO:
candidate = new Point3f(pe.getToX(), pe.getToY(), pe.getToZ());
break;
case LINE_TO:
candidate = (new Segment3f(pe.getFromX(), pe.getFromY(), pe.getFromZ(), pe.getToX(), pe.getToY(), pe.getToZ())).getClosestPointTo(new Point3f(x,y,z));
break;
case CLOSE:
if (!pe.isEmpty()) {
candidate = (new Segment3f(pe.getFromX(), pe.getFromY(), pe.getFromZ(), pe.getToX(), pe.getToY(), pe.getToZ())).getClosestPointTo(new Point3f(x,y,z));
}
break;
case QUAD_TO:
subPath = new Path3f();
subPath.moveTo(pe.getFromX(), pe.getFromY(), pe.getFromZ());
subPath.quadTo(
pe.getCtrlX1(), pe.getCtrlY1(), pe.getCtrlZ1(),
pe.getToX(), pe.getToY(), pe.getToZ());
candidate = subPath.getClosestPointTo(new Point3f(x,y,z));
break;
case CURVE_TO:
subPath = new Path3f();
subPath.moveTo(pe.getFromX(), pe.getFromY(), pe.getFromZ());
subPath.curveTo(
pe.getCtrlX1(), pe.getCtrlY1(), pe.getCtrlZ1(),
pe.getCtrlX2(), pe.getCtrlY2(), pe.getCtrlZ2(),
pe.getToX(), pe.getToY(), pe.getToZ());
candidate = subPath.getClosestPointTo(new Point3f(x,y,z));
break;
default:
throw new IllegalStateException(
pe.type==null ? null : pe.type.toString());
}
if (candidate!=null) {
double d = candidate.getDistanceSquared(new Point3f(x,y,z));
if (d<bestDist) {
bestDist = d;
closest = candidate;
}
}
}
return closest;
}
/** Replies the point on the path that is farthest to the given point.
* <p>
* <strong>CAUTION:</strong> This function works only on path iterators
* that are replying polyline primitives, ie. if the
* {@link PathIterator3f#isPolyline()} of <var>pi</var> is replying
* <code>true</code>.
* {@link #getFarthestPointTo(Point3D)} avoids this restriction.
*
* @param pi is the iterator on the elements of the path.
* @param x
* @param y
* @param z
* @return the farthest point on the shape.
*/
public static Point3D getFarthestPointTo(PathIterator3f pathIterator, double x, double y, double z) {
Point3D farthest = null;
double bestDist = Double.NEGATIVE_INFINITY;
Point3D candidate;
AbstractPathElement3F pe = pathIterator.next();
Path3f subPath;
if (pe.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
candidate = new Point3f(pe.getToX(), pe.getToY(), pe.getToZ());
while (pathIterator.hasNext()) {
pe = pathIterator.next();
candidate = null;
switch(pe.type) {
case MOVE_TO:
candidate = new Point3f(pe.getToX(), pe.getToY(), pe.getToZ());
break;
case LINE_TO:
candidate = (new Segment3f(pe.getFromX(), pe.getFromY(), pe.getFromZ(), pe.getToX(), pe.getToY(), pe.getToZ())).getFarthestPointTo(new Point3f(x,y,z));
break;
case CLOSE:
if (!pe.isEmpty()) {
candidate = (new Segment3f(pe.getFromX(), pe.getFromY(), pe.getFromZ(), pe.getToX(), pe.getToY(), pe.getToZ())).getFarthestPointTo(new Point3f(x,y,z));
}
break;
case QUAD_TO:
subPath = new Path3f();
subPath.moveTo(pe.getFromX(), pe.getFromY(), pe.getFromZ());
subPath.quadTo(
pe.getCtrlX1(), pe.getCtrlY1(), pe.getCtrlZ1(),
pe.getToX(), pe.getToY(), pe.getToZ());
candidate = subPath.getFarthestPointTo(new Point3f(x,y,z));
break;
case CURVE_TO:
subPath = new Path3f();
subPath.moveTo(pe.getFromX(), pe.getFromY(), pe.getFromZ());
subPath.curveTo(
pe.getCtrlX1(), pe.getCtrlY1(), pe.getCtrlZ1(),
pe.getCtrlX2(), pe.getCtrlY2(), pe.getCtrlZ2(),
pe.getToX(), pe.getToY(), pe.getToZ());
candidate = subPath.getFarthestPointTo(new Point3f(x,y,z));
break;
default:
throw new IllegalStateException(
pe.type==null ? null : pe.type.toString());
}
if (candidate!=null) {
double d = candidate.getDistanceSquared(new Point3f(x,y,z));
if (d>bestDist) {
bestDist = d;
farthest = candidate;
}
}
}
return farthest;
}
private static boolean buildGraphicalBoundingBox(PathIterator3f iterator, AlignedBox3f box) {
boolean foundOneLine = false;
double xmin = Double.POSITIVE_INFINITY;
double ymin = Double.POSITIVE_INFINITY;
double zmin = Double.POSITIVE_INFINITY;
double xmax = Double.NEGATIVE_INFINITY;
double ymax = Double.NEGATIVE_INFINITY;
double zmax = Double.NEGATIVE_INFINITY;
AbstractPathElement3F element;
Path3f subPath;
while (iterator.hasNext()) {
element = iterator.next();
switch(element.type) {
case LINE_TO:
if (element.getFromX()<xmin) xmin = element.getFromX();
if (element.getFromY()<ymin) ymin = element.getFromY();
if (element.getFromZ()<zmin) zmin = element.getFromZ();
if (element.getFromX()>xmax) xmax = element.getFromX();
if (element.getFromY()>ymax) ymax = element.getFromY();
if (element.getFromZ()>zmax) zmax = element.getFromZ();
if (element.getToX()<xmin) xmin = element.getToX();
if (element.getToY()<ymin) ymin = element.getToY();
if (element.getToZ()<zmin) zmin = element.getToZ();
if (element.getToX()>xmax) xmax = element.getToX();
if (element.getToY()>ymax) ymax = element.getToY();
if (element.getToZ()>zmax) zmax = element.getToZ();
foundOneLine = true;
break;
case CURVE_TO:
subPath = new Path3f();
subPath.moveTo(element.getFromX(), element.getFromY(), element.getFromZ());
subPath.curveTo(
element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(),
element.getCtrlX2(), element.getCtrlY2(), element.getCtrlZ2(),
element.getToX(), element.getToY(), element.getToZ());
if (buildGraphicalBoundingBox(
subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
box)) {
if (box.getMinX()<xmin) xmin = box.getMinX();
if (box.getMinY()<ymin) ymin = box.getMinY();
if (box.getMinZ()<zmin) zmin = box.getMinZ();
if (box.getMaxX()>xmax) xmax = box.getMaxX();
if (box.getMinY()>ymax) ymax = box.getMinY();
if (box.getMinZ()>zmax) zmax = box.getMinZ();
foundOneLine = true;
}
break;
case QUAD_TO:
subPath = new Path3f();
subPath.moveTo(element.getFromX(), element.getFromY(), element.getFromZ());
subPath.quadTo(
element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(),
element.getToX(), element.getToY(), element.getToZ());
if (buildGraphicalBoundingBox(
subPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
box)) {
if (box.getMinX()<xmin) xmin = box.getMinX();
if (box.getMinY()<ymin) ymin = box.getMinY();
if (box.getMinZ()<zmin) zmin = box.getMinZ();
if (box.getMaxX()>xmax) xmax = box.getMaxX();
if (box.getMinY()>ymax) ymax = box.getMinY();
if (box.getMinZ()>zmax) zmax = box.getMinZ();
foundOneLine = true;
}
break;
case MOVE_TO:
case CLOSE:
default:
}
}
if (foundOneLine) {
box.setFromCorners(xmin, ymin, zmin, xmax, ymax, zmax);
}
else {
box.clear();
}
return foundOneLine;
}
private boolean buildLogicalBoundingBox(AlignedBox3f box) {
if (this.numCoords>0) {
double xmin = Double.POSITIVE_INFINITY;
double ymin = Double.POSITIVE_INFINITY;
double zmin = Double.POSITIVE_INFINITY;
double xmax = Double.NEGATIVE_INFINITY;
double ymax = Double.NEGATIVE_INFINITY;
double zmax = Double.NEGATIVE_INFINITY;
for(int i=0; i<this.numCoords; i+= 3) {
if (this.coords[i]<xmin) xmin = this.coords[i];
if (this.coords[i+1]<ymin) ymin = this.coords[i+1];
if (this.coords[i+2]<zmin) zmin = this.coords[i+2];
if (this.coords[i]>xmax) xmax = this.coords[i];
if (this.coords[i+1]>ymax) ymax = this.coords[i+1];
if (this.coords[i+2]>zmax) zmax = this.coords[i+2];
}
box.setFromCorners(xmin, ymin, zmin, xmax, ymax, zmax);
return true;
}
return false;
}
//---------------------------------------------------------------
/** Array of types.
*/
PathElementType[] types;
/** Array of coords.
*/
double[] coords;
/** Number of types in the array.
*/
int numTypes = 0;
/** Number of coords in the array.
*/
int numCoords = 0;
/** 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
* when the path does not represents a drawable path
* (a path with a line or a curve).
*/
private Boolean isEmpty = Boolean.TRUE;
/** Indicates if the path contains base primitives
* (no curve).
*/
private Boolean isPolyline = Boolean.TRUE;
/** 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<AlignedBox3f> graphicalBounds = null;
/** Buffer for the bounds of the path that corresponds
* to all the points added in the path.
*/
private SoftReference<AlignedBox3f> logicalBounds = null;
/**
*/
public Path3f() {
this(PathWindingRule.NON_ZERO);
}
/**
* @param iterator
*/
public Path3f(Iterator<AbstractPathElement3F> iterator) {
this(PathWindingRule.NON_ZERO, iterator);
}
/**
* @param windingRule1
*/
public Path3f(PathWindingRule windingRule1) {
assert(windingRule1!=null);
this.types = new PathElementType[GROW_SIZE];
this.coords = new double[GROW_SIZE];
this.windingRule = windingRule1;
}
/**
* @param windingRule1
* @param iterator
*/
public Path3f(PathWindingRule windingRule1, Iterator<AbstractPathElement3F> iterator) {
assert(windingRule1!=null);
this.types = new PathElementType[GROW_SIZE];
this.coords = new double[GROW_SIZE];
this.windingRule = windingRule1;
add(iterator);
}
/**
* @param p
*/
public Path3f(Path3f p) {
this.coords = p.coords.clone();
this.isEmpty = p.isEmpty;
this.isPolyline = p.isPolyline;
this.numCoords = p.numCoords;
this.numTypes = p.numTypes;
this.types = p.types.clone();
this.windingRule = p.windingRule;
AlignedBox3f box;
box = p.graphicalBounds==null ? null : p.graphicalBounds.get();
if (box!=null) {
this.graphicalBounds = new SoftReference<>(box.clone());
}
box = p.logicalBounds==null ? null : p.logicalBounds.get();
if (box!=null) {
this.logicalBounds = new SoftReference<>(box.clone());
}
}
/** Remove the point with the given coordinates.
*
* @param x
* @param y
* @param z
* @return <code>true</code> if the point was removed; <code>false</code> otherwise.
*/
boolean remove(double x, double y, double z) {
for(int i=0, j=0; i<this.numCoords && j<this.numTypes;) {
switch(this.types[j]) {
case MOVE_TO:
case LINE_TO:
if (x==this.coords[i] && y==this.coords[i+1] && z==this.coords[i+2]) {
this.numCoords -= 3;
--this.numTypes;
System.arraycopy(this.coords, i+3, this.coords, i, this.numCoords);
System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
this.isEmpty = null;
return true;
}
i += 3;
++j;
break;
case CURVE_TO:
if ((x==this.coords[i] && y==this.coords[i+1] && z==this.coords[i+2])
||(x==this.coords[i+3] && y==this.coords[i+4] && z==this.coords[i+5])
||(x==this.coords[i+6] && y==this.coords[i+7] && z==this.coords[i+8])) {
this.numCoords -= 9;
--this.numTypes;
System.arraycopy(this.coords, i+9, this.coords, i, this.numCoords);
System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
this.isEmpty = null;
this.isPolyline = null;
return true;
}
i += 9;
++j;
break;
case QUAD_TO:
if ((x==this.coords[i] && y==this.coords[i+1] && z==this.coords[i+2])
||(x==this.coords[i+3] && y==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);
System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
this.isEmpty = null;
this.isPolyline = null;
return true;
}
i += 6;
++j;
break;
case CLOSE:
++j;
break;
default:
break;
}
}
return false;
}
/** Replies if the given points exists in the coordinates of this path.
*
* @param p
* @return <code>true</code> if the point is a control point of the path.
*/
@Pure
boolean containsControlPoint(Point3D p) {
double x, y, z;
for(int i=0; i<this.numCoords;) {
x = this.coords[i++];
y = this.coords[i++];
z = this.coords[i++];
if (x==p.getX() && y==p.getY() && z==p.getZ()) {
return true;
}
}
return false;
}
@Pure
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("[");
if (this.numCoords>0) {
b.append(this.coords[0]);
for(int i=1; i<this.numCoords; ++i) {
b.append(", ");
b.append(this.coords[i]);
}
}
b.append("]");
return b.toString();
}
@Override
public boolean isEmpty() {
if (this.isEmpty==null) {
this.isEmpty = Boolean.TRUE;
PathIterator3f pi = getPathIterator();
AbstractPathElement3F pe;
while (this.isEmpty==Boolean.TRUE && pi.hasNext()) {
pe = pi.next();
if (pe.isDrawable()) {
this.isEmpty = Boolean.FALSE;
}
}
}
return this.isEmpty.booleanValue();
}
@Override
public void clear() {
this.types = new PathElementType[GROW_SIZE];
this.coords = new double[GROW_SIZE];
this.windingRule = PathWindingRule.NON_ZERO;
this.numCoords = 0;
this.numTypes = 0;
this.isEmpty = Boolean.TRUE;
this.isPolyline = Boolean.TRUE;
this.graphicalBounds = null;
this.logicalBounds = null;
}
@Pure
@Override
public Path3f clone() {
Path3f clone = super.clone();
clone.coords = this.coords.clone();
clone.types = this.types.clone();
clone.windingRule = this.windingRule;
return clone;
}
@Pure
@Override
public Point3D getClosestPointTo(Point3D p) {
return getClosestPointTo(
getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
p.getX(), p.getY(),p.getZ());
}
@Pure
@Override
public Point3D getFarthestPointTo(Point3D p) {
return getFarthestPointTo(
getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
p.getX(), p.getY(), p.getZ());
}
@Override
public void set(Shape3F s) {
clear();
add(s.getPathIterator());
}
@Override
public AlignedBox3f toBoundingBox() {
AlignedBox3f bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
if (bb==null) {
bb = new AlignedBox3f();
buildGraphicalBoundingBox(
getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
bb);
this.graphicalBounds = new SoftReference<>(bb);
}
return bb;
}
/** 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 AlignedBox3f toBoundingBoxWithCtrlPoints() {
AlignedBox3f bb = this.logicalBounds==null ? null : this.logicalBounds.get();
if (bb==null) {
bb = new AlignedBox3f();
buildLogicalBoundingBox(bb);
this.logicalBounds = new SoftReference<>(bb);
}
return bb;
}
@Override
public void toBoundingBox(AbstractBoxedShape3F<?> box) {
AlignedBox3f bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
if (bb==null) {
bb = new AlignedBox3f();
buildGraphicalBoundingBox(
getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
bb);
this.graphicalBounds = new SoftReference<>(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.
* @see #toBoundingBox()
*/
public void toBoundingBoxWithCtrlPoints(AlignedBox3f box) {
AlignedBox3f bb = this.logicalBounds==null ? null : this.logicalBounds.get();
if (bb==null) {
bb = new AlignedBox3f();
buildLogicalBoundingBox(bb);
this.logicalBounds = new SoftReference<>(bb);
}
box.set(bb);
}
@Pure
@Override
public double distanceSquared(Point3D p) {
Point3D c = getClosestPointTo(p);
return c.getDistanceSquared(p);
}
@Pure
@Override
public double distanceL1(Point3D p) {
Point3D c = getClosestPointTo(p);
return c.getDistanceL1(p);
}
@Pure
@Override
public double distanceLinf(Point3D p) {
Point3D c = getClosestPointTo(p);
return c.getDistanceLinf(p);
}
/** Transform the current path.
* This function changes the current path.
*
* @param transform is the spatial transformation to apply.
* @see #createTransformedShape(Transform3D)
*/
@Override
public void transform(Transform3D transform) {
if (transform!=null) {
Point3D p = new Point3f();
for(int i=0; i<this.numCoords;) {
p.set(this.coords[i], this.coords[i+1], this.coords[i+2]);
transform.transform(p);
this.coords[i++] = p.getX();
this.coords[i++] = p.getY();
this.coords[i++] = p.getZ();
}
this.graphicalBounds = null;
this.logicalBounds = null;
}
}
@Pure
@Override
public Shape3F createTransformedShape(Transform3D transform) {
Path3f newP = new Path3f(this);
newP.transform(transform);
return newP;
}
@Override
public void translate(double dx, double dy, double dz) {
for(int i=0; i<this.numCoords;) {
this.coords[i++] += dx;
this.coords[i++] += dy;
this.coords[i++] += dz;
}
AlignedBox3f bb;
bb = this.logicalBounds==null ? null : this.logicalBounds.get();
if (bb!=null) bb.translate(dx, dy, dz);
bb = this.graphicalBounds==null ? null : this.graphicalBounds.get();
if (bb!=null) bb.translate(dx, dy, dz);
}
@Pure
@Override
public boolean intersects(AbstractBoxedShape3F<?> s) {
if (s.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = AbstractSegment3F.intersectsSegmentAlignedBox(
curx, cury, curz,
endx, endy, endz,
s.getMinX(), s.getMinY(), s.getMinZ(),
s.getMaxX(), s.getMaxY(), s.getMaxZ());
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = AbstractSegment3F.intersectsSegmentAlignedBox(
curx, cury, curz,
movx, movy, movz,
s.getMinX(), s.getMinY(), s.getMinZ(),
s.getMaxX(), s.getMaxY(), s.getMaxZ());
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
public boolean intersects(Path3f p) {
if (p.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = p.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(p.clone());
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(p.clone());
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = p.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(Path3d p) {
if (p.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3d pi = getPathIteratorProperty(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3D pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3d subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = p.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3d();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(p.clone());
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3d();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(p.clone());
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = p.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(AbstractSphere3F s) {
if (s.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = s.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = s.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(AbstractSegment3F s) {
if (s.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = s.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = s.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(AbstractTriangle3F triangle) {
if (triangle.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = triangle.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(triangle);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(triangle);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = triangle.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(AbstractCapsule3F s) {
if (s.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = s.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = s.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
@Override
public boolean intersects(AbstractOrientedBox3F s) {
if (s.isEmpty()) return false;
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = s.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(s);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = s.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Pure
public boolean intersects(Plane3D<?> p) {
int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
boolean intersects = false;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext() && intersects==false) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = cury = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
intersects = p.intersects(new Segment3f(
curx, cury, curz,
endx, endy, endz));
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
intersects = subPath.intersects(p);
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury,curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
intersects = subPath.intersects(p);
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
intersects = p.intersects(new Segment3f(
curx, cury, curz,
movx, movy, movz));
}
curx = movx;
cury = movy;
curz = movz;
break;
default:
}
}
return intersects && (mask!=0);
}
@Override
public boolean isPolyline() {
if (this.isPolyline==null) {
this.isPolyline = Boolean.TRUE;
PathIterator3f pi = getPathIterator();
AbstractPathElement3F pe;
PathElementType t;
while (this.isPolyline==Boolean.TRUE && pi.hasNext()) {
pe = pi.next();
t = pe.getType();
if (t==PathElementType.CURVE_TO || t==PathElementType.QUAD_TO) {
this.isPolyline = Boolean.FALSE;
}
}
}
return this.isPolyline.booleanValue();
}
/**
* {@inheritDoc}
*/
@Pure
@Override
public boolean contains(double x, double y, double z) {
AlignedBox3f ab = this.toBoundingBox();
return ab.contains(new Point3f(x,y,z));
}
@Pure
public boolean equals(Path3f path) {
return (this.numCoords==path.numCoords
&& this.numTypes==path.numTypes
&& Arrays.equals(this.coords, path.coords)
&& Arrays.equals(this.types, path.types)
&& this.windingRule==path.windingRule);
}
@Pure
@Override
public boolean equals(Object obj) {
if (obj instanceof Path3d) {
Path3d path = (Path3d)obj;
return (this.numCoords==path.numCoordsProperty.get()
&& this.numTypes==path.numTypesProperty.get()
&& this.toString().equals(path.toString())
&& Arrays.equals(this.types, path.types)
&& this.windingRule==path.windingRule);
}
else if (obj instanceof Path3f) {
Path3f path = (Path3f)obj;
return (this.numCoords==path.numCoords
&& this.numTypes==path.numTypes
&& Arrays.equals(this.coords, path.coords)
&& Arrays.equals(this.types, path.types)
&& this.windingRule==path.windingRule);
}
return false;
}
@Pure
@Override
public int hashCode() {
long bits = 1L;
bits = 31L * bits + this.numCoords;
bits = 31L * bits + this.numTypes;
bits = 31L * bits + Arrays.hashCode(this.coords);
bits = 31L * bits + Arrays.hashCode(this.types);
bits = 31L * bits + this.windingRule.ordinal();
return (int) (bits ^ (bits >> 32));
}
@Pure
@Override
public PathIterator3f getPathIterator(double flatness) {
return new FlatteningPathIterator3f(getWindingRule(), getPathIterator(null), flatness, 10);
}
/** Replies an iterator on the path elements.
* <p>
* Only {@link PathElementType#MOVE_TO},
* {@link PathElementType#LINE_TO}, and
* {@link PathElementType#CLOSE} types are returned by the iterator.
* <p>
* The amount of subdivision of the curved segments is controlled by the
* flatness parameter, which specifies the maximum distance that any point
* on the unflattened transformed curve can deviate from the returned
* flattened path segments. Note that a limit on the accuracy of the
* flattened path might be silently imposed, causing very small flattening
* parameters to be treated as larger values. This limit, if there is one,
* is defined by the particular implementation that is used.
* <p>
* The iterator for this class is not multi-threaded safe.
*
* @param transform is an optional affine Transform3D to be applied to the
* coordinates as they are returned in the iteration, or <code>null</code> if
* untransformed coordinates are desired.
* @param flatness is the maximum distance that the line segments used to approximate
* the curved segments are allowed to deviate from any point on the original curve.
* @return an iterator on the path elements.
*/
@Pure
public PathIterator3f getPathIterator(Transform3D transform, double flatness) {
return new FlatteningPathIterator3f(getWindingRule(), getPathIterator(transform), flatness, 10);
}
/** {@inheritDoc}
*/
@Pure
@Override
public PathIterator3f getPathIterator(Transform3D transform) {
if (transform == null) {
return new CopyPathIterator3f();
}
return new TransformPathIterator3f(transform);
}
@Pure
@Override
public PathIterator3d getPathIteratorProperty(double flatness) {
return new Path3d.FlatteningPathIterator3d(getWindingRule(), getPathIteratorProperty(null), flatness, 10);
}
/** Replies an iterator on the path elements.
* <p>
* Only {@link PathElementType#MOVE_TO},
* {@link PathElementType#LINE_TO}, and
* {@link PathElementType#CLOSE} types are returned by the iterator.
* <p>
* The amount of subdivision of the curved segments is controlled by the
* flatness parameter, which specifies the maximum distance that any point
* on the unflattened transformed curve can deviate from the returned
* flattened path segments. Note that a limit on the accuracy of the
* flattened path might be silently imposed, causing very small flattening
* parameters to be treated as larger values. This limit, if there is one,
* is defined by the particular implementation that is used.
* <p>
* The iterator for this class is not multi-threaded safe.
*
* @param transform is an optional affine Transform3D to be applied to the
* coordinates as they are returned in the iteration, or <code>null</code> if
* untransformed coordinates are desired.
* @param flatness is the maximum distance that the line segments used to approximate
* the curved segments are allowed to deviate from any point on the original curve.
* @return an iterator on the path elements.
*/
@Pure
public PathIterator3d getPathIteratorProperty(Transform3D transform, double flatness) {
return new Path3d.FlatteningPathIterator3d(getWindingRule(), getPathIteratorProperty(transform), flatness, 10);
}
@Pure
@Override
public PathIterator3d getPathIteratorProperty(Transform3D transform) {
if (transform == null) {
return new CopyPathIterator3d();
}
return new TransformPathIterator3d(transform);
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return this.windingRule;
}
/** Set the winding rule for the path.
*
* @param r is the winding rule for the path.
*/
public void setWindingRule(PathWindingRule r) {
assert(r!=null);
this.windingRule = r;
}
/** Add the elements replied by the iterator into this path.
*
* @param iterator
*/
public void add(Iterator<AbstractPathElement3F> iterator) {
AbstractPathElement3F element;
while (iterator.hasNext()) {
element = iterator.next();
switch(element.type) {
case MOVE_TO:
moveTo(element.getToX(), element.getToY(), element.getToZ());
break;
case LINE_TO:
lineTo(element.getToX(), element.getToY(), element.getToZ());
break;
case QUAD_TO:
quadTo(element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(), element.getToX(), element.getToY(), element.getToZ());
break;
case CURVE_TO:
curveTo(element.getCtrlX1(), element.getCtrlY1(), element.getCtrlZ1(), element.getCtrlX2(), element.getCtrlY2(), element.getCtrlZ2(), element.getToX(), element.getToY(), element.getToZ());
break;
case CLOSE:
closePath();
break;
default:
}
}
}
/** Remove the last action.
*/
public void removeLast() {
if (this.numTypes>0) {
switch(this.types[this.numTypes-1]) {
case CLOSE:
// no coord to remove
break;
case MOVE_TO:
case LINE_TO:
this.numCoords -= 3;
break;
case CURVE_TO:
this.numCoords -= 9;
this.isPolyline = null;
break;
case QUAD_TO:
this.numCoords -= 6;
this.isPolyline = null;
break;
default:
throw new IllegalStateException();
}
--this.numTypes;
this.isEmpty = null;
this.graphicalBounds = null;
this.logicalBounds = null;
}
}
/** Change the coordinates of the last inserted point.
*
* @param x
* @param y
* @param z
*/
public void setLastPoint(double x, double y, double z) {
if (this.numCoords>=3) {
this.coords[this.numCoords-3] = x;
this.coords[this.numCoords-2] = y;
this.coords[this.numCoords-1] = z;
this.graphicalBounds = null;
this.logicalBounds = null;
}
}
private void ensureSlots(boolean needMove, int n) {
if (needMove && this.numTypes==0) {
throw new IllegalStateException("missing initial moveto in path definition");
}
if (this.types.length==this.numTypes) {
this.types = Arrays.copyOf(this.types, this.types.length+GROW_SIZE);
}
while ((this.numCoords+n)>=this.coords.length) {
this.coords = Arrays.copyOf(this.coords, this.coords.length+GROW_SIZE);
}
}
/**
* Adds a point to the path by moving to the specified
* coordinates specified in double precision.
*
* @param x the specified X coordinate
* @param y the specified Y coordinate
* @param z the specified Z coordinate
*/
public void moveTo(double x, double y, double z) {
if (this.numTypes>0 && this.types[this.numTypes-1]==PathElementType.MOVE_TO) {
this.coords[this.numCoords-3] = x;
this.coords[this.numCoords-2] = y;
this.coords[this.numCoords-1] = z;
}
else {
ensureSlots(false, 3);
this.types[this.numTypes++] = PathElementType.MOVE_TO;
this.coords[this.numCoords++] = x;
this.coords[this.numCoords++] = y;
this.coords[this.numCoords++] = z;
}
this.graphicalBounds = null;
this.logicalBounds = null;
}
/**
* Adds a point to the path by drawing a straight line from the
* current coordinates to the new specified coordinates
* specified in double precision.
*
* @param x the specified X coordinate
* @param y the specified Y coordinate
* @param z the specified Z coordinate
*/
public void lineTo(double x, double y, double z) {
ensureSlots(true, 3);
this.types[this.numTypes++] = PathElementType.LINE_TO;
this.coords[this.numCoords++] = x;
this.coords[this.numCoords++] = y;
this.coords[this.numCoords++] = z;
this.isEmpty = null;
this.graphicalBounds = null;
this.logicalBounds = null;
}
/**
* Adds a curved segment, defined by two new points, to the path by
* drawing a Quadratic curve that intersects both the current
* coordinates and the specified coordinates {@code (x2,y2,z2)},
* using the specified point {@code (x1,y1,z1)} as a quadratic
* parametric control point.
* All coordinates are specified in double precision.
*
* @param x1 the X coordinate of the quadratic control point
* @param y1 the Y coordinate of the quadratic control point
* @param z1 the Z coordinate of the quadratic control point
* @param x2 the X coordinate of the final end point
* @param y2 the Y coordinate of the final end point
* @param z2 the Z coordinate of the final end point
*/
public void quadTo(double x1, double y1, double z1, double x2, double y2, double z2) {
ensureSlots(true, 6);
this.types[this.numTypes++] = PathElementType.QUAD_TO;
this.coords[this.numCoords++] = x1;
this.coords[this.numCoords++] = y1;
this.coords[this.numCoords++] = z1;
this.coords[this.numCoords++] = x2;
this.coords[this.numCoords++] = y2;
this.coords[this.numCoords++] = z2;
this.isEmpty = null;
this.isPolyline = Boolean.FALSE;
this.graphicalBounds = null;
this.logicalBounds = null;
}
/**
* Adds a curved segment, defined by three new points, to the path by
* drawing a Bézier curve that intersects both the current
* coordinates and the specified coordinates {@code (x3,y3,z3)},
* using the specified points {@code (x1,y1,z1)} and {@code (x2,y2,z2)} as
* Bézier control points.
* All coordinates are specified in double precision.
*
* @param x1 the X coordinate of the first Bézier control point
* @param y1 the Y coordinate of the first Bézier control point
* @param z1 the Z coordinate of the first Bézier control point
* @param x2 the X coordinate of the second Bézier control point
* @param y2 the Y coordinate of the second Bézier control point
* @param z2 the Z coordinate of the second Bézier control point
* @param x3 the X coordinate of the final end point
* @param y3 the Y coordinate of the final end point
* @param z3 the Z coordinate of the final end point
*/
public void curveTo(double x1, double y1, double z1,
double x2, double y2, double z2,
double x3, double y3, double z3) {
ensureSlots(true, 9);
this.types[this.numTypes++] = PathElementType.CURVE_TO;
this.coords[this.numCoords++] = x1;
this.coords[this.numCoords++] = y1;
this.coords[this.numCoords++] = z1;
this.coords[this.numCoords++] = x2;
this.coords[this.numCoords++] = y2;
this.coords[this.numCoords++] = z2;
this.coords[this.numCoords++] = x3;
this.coords[this.numCoords++] = y3;
this.coords[this.numCoords++] = z3;
this.isEmpty = null;
this.isPolyline = Boolean.FALSE;
this.graphicalBounds = null;
this.logicalBounds = null;
}
/**
* Closes the current subpath by drawing a straight line back to
* the coordinates of the last {@code moveTo}. If the path is already
* closed or if the previous coordinates are for a {@code moveTo}
* then this method has no effect.
*/
public void closePath() {
if (this.numTypes<=0 ||
(this.types[this.numTypes-1]!=PathElementType.CLOSE
&&this.types[this.numTypes-1]!=PathElementType.MOVE_TO)) {
ensureSlots(true, 0);
this.types[this.numTypes++] = PathElementType.CLOSE;
}
}
/** Replies the number of points in the path.
*
* @return the number of points in the path.
*/
@Pure
public int size() {
return this.numCoords/3;
}
/** Replies the total lentgh of the path.
* The length() method doesn't take into account the moveTo deplacement to calculate length.
* It only calculate the lines can be drawed.
*
* @return the length of the path.
*/
//FIXME TO BE IMPLEMENTED IN POLYLINE
public double length() {
if (this.isEmpty()) return 0;
double length = 0;
PathIterator3f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
AbstractPathElement3F pathElement = pi.next();
if (pathElement.type != PathElementType.MOVE_TO) {
throw new IllegalArgumentException("missing initial moveto in path definition");
}
Path3f subPath;
double curx, cury, curz, movx, movy, movz, endx, endy, endz;
curx = movx = pathElement.getToX();
cury = movy = pathElement.getToY();
curz = movz = pathElement.getToZ();
while (pi.hasNext()) {
pathElement = pi.next();
switch (pathElement.type) {
case MOVE_TO:
movx = curx = pathElement.getToX();
movy = cury = pathElement.getToY();
movz = curz = pathElement.getToZ();
break;
case LINE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
length += FunctionalPoint3D.distancePointPoint(
curx, cury, curz,
endx, endy, endz);
curx = endx;
cury = endy;
curz = endz;
break;
case QUAD_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury, curz);
subPath.quadTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
endx, endy, endz);
length += subPath.length();
curx = endx;
cury = endy;
curz = endz;
break;
case CURVE_TO:
endx = pathElement.getToX();
endy = pathElement.getToY();
endz = pathElement.getToZ();
subPath = new Path3f();
subPath.moveTo(curx, cury, curz);
subPath.curveTo(
pathElement.getCtrlX1(), pathElement.getCtrlY1(), pathElement.getCtrlZ1(),
pathElement.getCtrlX2(), pathElement.getCtrlY2(), pathElement.getCtrlZ2(),
endx, endy, endz);
length += subPath.length();
curx = endx;
cury = endy;
curz = endz;
break;
case CLOSE:
if (curx != movx || cury != movy || curz != movz) {
length += FunctionalPoint3D.distancePointPoint(
curx, cury, curz,
movx, movy, movz);
}
curx = movx;
cury = movy;
cury = movz;
break;
default:
}
}
return length;
}
/** Replies the coordinates of this path in an array of
* double precision floating-point numbers.
*
* @return the coordinates.
*/
@Pure
public final double[] toDoubleArray() {
return toDoubleArray(null);
}
/** Replies the coordinates of this path in an array of
* double precision floating-point numbers.
*
* @param transform is the transformation to apply to all the coordinates.
* @return the coordinates.
*/
@Pure
public double[] toDoubleArray(Transform3D transform) {
double[] clone = new double[this.numCoords];
if (transform==null) {
for(int i=0; i<this.numCoords; ++i) {
clone[i] = this.coords[i];
}
}
else {
Point3f p = new Point3f();
for(int i=0; i<clone.length;) {
p.x = this.coords[i];
p.y = this.coords[i+1];
p.y = this.coords[i+2];
transform.transform(p);
clone[i++] = p.x;
clone[i++] = p.y;
clone[i++] = p.z;
}
}
return clone;
}
/** Replies the points of this path in an array.
*
* @return the points.
*/
@Pure
public final Point3D[] toPointArray() {
return toPointArray(null);
}
/** Replies the points of this path in an array.
*
* @param transform is the transformation to apply to all the points.
* @return the points.
*/
@Pure
public Point3D[] toPointArray(Transform3D transform) {
Point3D[] clone = new Point3D[this.numCoords/3];
if (transform==null) {
for(int i=0, j=0; j<this.numCoords; ++i) {
clone[i] = new Point3f(
this.coords[j++],
this.coords[j++],
this.coords[j++]);
}
}
else {
for(int i=0, j=0; j<clone.length; ++i) {
clone[i] = new Point3f(
this.coords[j++],
this.coords[j++],
this.coords[j++]);
transform.transform(clone[i]);
}
}
return clone;
}
/** Replies the point at the given index.
* The index is in [0;{@link #size()}).
*
* @param index
* @return the point at the given index.
*/
@Pure
public Point3f getPointAt(int index) {
return new Point3f(
this.coords[index*3],
this.coords[index*3+1],
this.coords[index*3+2]);
}
/** Replies the last point in the path.
*
* @return the last point.
*/
@Pure
public Point3f getCurrentPoint() {
return new Point3f(
this.coords[this.numCoords-3],
this.coords[this.numCoords-2],
this.coords[this.numCoords-1]);
}
//-----------------------------------------------------------------------------
/** A path iterator that does not transform the coordinates.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class CopyPathIterator3f implements PathIterator3f {
private final Point3D p1 = new Point3f();
private final Point3D p2 = new Point3f();
private int iType = 0;
private int iCoord = 0;
private double movex, movey, movez;
/**
*/
public CopyPathIterator3f() {
//
}
@Pure
@Override
public boolean hasNext() {
return this.iType<Path3f.this.numTypes;
}
@Override
public AbstractPathElement3F next() {
int type = this.iType;
if (this.iType>=Path3f.this.numTypes) {
throw new NoSuchElementException();
}
AbstractPathElement3F element = null;
switch(Path3f.this.types[type]) {
case MOVE_TO:
if (this.iCoord+3>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.movex = Path3f.this.coords[this.iCoord++];
this.movey = Path3f.this.coords[this.iCoord++];
this.movez = Path3f.this.coords[this.iCoord++];
this.p2.set(this.movex, this.movey, this.movez);
element = new AbstractPathElement3F.MovePathElement3f(
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case LINE_TO:
if (this.iCoord+3>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
element = new AbstractPathElement3F.LinePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case QUAD_TO:
{
if (this.iCoord+6>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
double ctrlx = Path3f.this.coords[this.iCoord++];
double ctrly = Path3f.this.coords[this.iCoord++];
double ctrlz = Path3f.this.coords[this.iCoord++];
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
element = new AbstractPathElement3F.QuadPathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
ctrlx, ctrly, ctrlz,
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CURVE_TO:
{
if (this.iCoord+9>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
double ctrlx1 = Path3f.this.coords[this.iCoord++];
double ctrly1 = Path3f.this.coords[this.iCoord++];
double ctrlz1 = Path3f.this.coords[this.iCoord++];
double ctrlx2 = Path3f.this.coords[this.iCoord++];
double ctrly2 = Path3f.this.coords[this.iCoord++];
double ctrlz2 = Path3f.this.coords[this.iCoord++];
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
element = new AbstractPathElement3F.CurvePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
ctrlx1, ctrly1, ctrlz1,
ctrlx2, ctrly2, ctrlz2,
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CLOSE:
this.p1.set(this.p2);
this.p2.set(this.movex, this.movey, this.movez);
element = new AbstractPathElement3F.ClosePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
default:
}
if (element==null)
throw new NoSuchElementException();
++this.iType;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return Path3f.this.getWindingRule();
}
@Override
public boolean isPolyline() {
return Path3f.this.isPolyline();
}
} // class CopyPathIterator3f
/** A path iterator that does not transform the coordinates.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class CopyPathIterator3d implements PathIterator3d {
private final Point3D p1 = new Point3f();
private final Point3D p2 = new Point3f();
private IntegerProperty iTypeProperty = new SimpleIntegerProperty(0);
private IntegerProperty iCoordProperty = new SimpleIntegerProperty(0);
private DoubleProperty movexProperty, moveyProperty, movezProperty;
/**
*/
public CopyPathIterator3d() {
this.movexProperty = new SimpleDoubleProperty();
this.moveyProperty = new SimpleDoubleProperty();
this.movezProperty = new SimpleDoubleProperty();
}
@Pure
@Override
public boolean hasNext() {
return this.iTypeProperty.get()<Path3f.this.numTypes;
}
@Override
public AbstractPathElement3D next() {
int type = this.iTypeProperty.get();
if (this.iTypeProperty.get()>=Path3f.this.numTypes) {
throw new NoSuchElementException();
}
AbstractPathElement3D element = null;
switch(Path3f.this.types[type]) {
case MOVE_TO:
if (this.iCoordProperty.get()+3>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.movexProperty.set(Path3f.this.coords[this.iCoordProperty.get()]);
this.moveyProperty.set(Path3f.this.coords[this.iCoordProperty.get()+1]);
this.movezProperty.set(Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.p2.set(this.movexProperty.get(), this.moveyProperty.get(), this.movezProperty.get());
element = new AbstractPathElement3D.MovePathElement3d(
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case LINE_TO:
if (this.iCoordProperty.get()+3>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
element = new AbstractPathElement3D.LinePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case QUAD_TO:
{
if (this.iCoordProperty.get()+6>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
double ctrlx = Path3f.this.coords[this.iCoordProperty.get()];
double ctrly = Path3f.this.coords[this.iCoordProperty.get()+1];
double ctrlz = Path3f.this.coords[this.iCoordProperty.get()+2];
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
element = new AbstractPathElement3D.QuadPathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
ctrlx, ctrly, ctrlz,
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CURVE_TO:
{
if (this.iCoordProperty.get()+9>Path3f.this.numCoords) {
throw new NoSuchElementException();
}
this.p1.set(this.p2);
double ctrlx1 = Path3f.this.coords[this.iCoordProperty.get()];
double ctrly1 = Path3f.this.coords[this.iCoordProperty.get()+1];
double ctrlz1 = Path3f.this.coords[this.iCoordProperty.get()+2];
double ctrlx2 = Path3f.this.coords[this.iCoordProperty.get()+3];
double ctrly2 = Path3f.this.coords[this.iCoordProperty.get()+4];
double ctrlz2 = Path3f.this.coords[this.iCoordProperty.get()+5];
this.iCoordProperty.set(this.iCoordProperty.get()+6);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
element = new AbstractPathElement3D.CurvePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
ctrlx1, ctrly1, ctrlz1,
ctrlx2, ctrly2, ctrlz2,
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CLOSE:
this.p1.set(this.p2);
this.p2.set(this.movexProperty.get(), this.moveyProperty.get(), this.movezProperty.get());
element = new AbstractPathElement3D.ClosePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
default:
}
if (element==null)
throw new NoSuchElementException();
this.iTypeProperty.set(this.iTypeProperty.get()+1);
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return Path3f.this.getWindingRule();
}
@Override
public boolean isPolyline() {
return Path3f.this.isPolyline();
}
} // class CopyPathIterator3d
/** A path iterator that transforms the coordinates.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class TransformPathIterator3f implements PathIterator3f {
private final Transform3D transform;
private final Point3D p1 = new Point3f();
private final Point3D p2 = new Point3f();
private final Point3D ptmp1 = new Point3f();
private final Point3D ptmp2 = new Point3f();
private int iType = 0;
private int iCoord = 0;
private double movex, movey, movez;
/**
* @param transform1
*/
public TransformPathIterator3f(Transform3D transform1) {
assert(transform1!=null);
this.transform = transform1;
}
@Pure
@Override
public boolean hasNext() {
return this.iType<Path3f.this.numTypes;
}
@Override
public AbstractPathElement3F next() {
if (this.iType>=Path3f.this.numTypes) {
throw new NoSuchElementException();
}
AbstractPathElement3F element = null;
switch(Path3f.this.types[this.iType++]) {
case MOVE_TO:
this.movex = Path3f.this.coords[this.iCoord++];
this.movey = Path3f.this.coords[this.iCoord++];
this.movez = Path3f.this.coords[this.iCoord++];
this.p2.set(this.movex, this.movey, this.movez);
this.transform.transform(this.p2);
element = new AbstractPathElement3F.MovePathElement3f(
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case LINE_TO:
this.p1.set(this.p2);
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.p2);
element = new AbstractPathElement3F.LinePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case QUAD_TO:
{
this.p1.set(this.p2);
this.ptmp1.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.ptmp1);
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.p2);
element = new AbstractPathElement3F.QuadPathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.ptmp1.getX(), this.ptmp1.getY(), this.ptmp1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CURVE_TO:
{
this.p1.set(this.p2);
this.ptmp1.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.ptmp1);
this.ptmp2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.ptmp2);
this.p2.set(
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++],
Path3f.this.coords[this.iCoord++]);
this.transform.transform(this.p2);
element = new AbstractPathElement3F.CurvePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.ptmp1.getX(), this.ptmp1.getY(), this.ptmp1.getZ(),
this.ptmp2.getX(), this.ptmp2.getY(), this.ptmp2.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CLOSE:
this.p1.set(this.p2);
this.p2.set(this.movex, this.movey, this.movez);
this.transform.transform(this.p2);
element = new AbstractPathElement3F.ClosePathElement3f(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
default:
}
if (element==null)
throw new NoSuchElementException();
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return Path3f.this.getWindingRule();
}
@Override
public boolean isPolyline() {
return Path3f.this.isPolyline();
}
} // class TransformPathIterator3f
/** A path iterator that transforms the coordinates.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class TransformPathIterator3d implements PathIterator3d {
private final Transform3D transform;
private final Point3D p1 = new Point3f();
private final Point3D p2 = new Point3f();
private final Point3D ptmp1 = new Point3f();
private final Point3D ptmp2 = new Point3f();
private IntegerProperty iTypeProperty = new SimpleIntegerProperty(0);
private IntegerProperty iCoordProperty = new SimpleIntegerProperty(0);
private DoubleProperty movexProperty, moveyProperty, movezProperty;
/**
* @param transform1
*/
public TransformPathIterator3d(Transform3D transform1) {
assert(transform1!=null);
this.transform = transform1;
this.movexProperty = new SimpleDoubleProperty();
this.moveyProperty = new SimpleDoubleProperty();
this.movezProperty = new SimpleDoubleProperty();
}
@Pure
@Override
public boolean hasNext() {
return this.iTypeProperty.get()<Path3f.this.numTypes;
}
@Override
public AbstractPathElement3D next() {
if (this.iTypeProperty.get()>=Path3f.this.numTypes) {
throw new NoSuchElementException();
}
AbstractPathElement3D element = null;
switch(Path3f.this.types[this.iTypeProperty.get()]) {
case MOVE_TO:
this.movexProperty.set(Path3f.this.coords[this.iCoordProperty.get()]);
this.iCoordProperty.set(this.iCoordProperty.get()+1);
this.moveyProperty.set(Path3f.this.coords[this.iCoordProperty.get()]);
this.iCoordProperty.set(this.iCoordProperty.get()+1);
this.movezProperty.set(Path3f.this.coords[this.iCoordProperty.get()]);
this.iCoordProperty.set(this.iCoordProperty.get()+1);
this.p2.set(this.movexProperty.get(), this.moveyProperty.get(), this.movezProperty.get());
this.transform.transform(this.p2);
element = new AbstractPathElement3D.MovePathElement3d(
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case LINE_TO:
this.p1.set(this.p2);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.p2);
element = new AbstractPathElement3D.LinePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
case QUAD_TO:
{
this.p1.set(this.p2);
this.ptmp1.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.ptmp1);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.p2);
element = new AbstractPathElement3D.QuadPathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.ptmp1.getX(), this.ptmp1.getY(), this.ptmp1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CURVE_TO:
{
this.p1.set(this.p2);
this.ptmp1.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.ptmp1);
this.ptmp2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.ptmp2);
this.p2.set(
Path3f.this.coords[this.iCoordProperty.get()],
Path3f.this.coords[this.iCoordProperty.get()+1],
Path3f.this.coords[this.iCoordProperty.get()+2]);
this.iCoordProperty.set(this.iCoordProperty.get()+3);
this.transform.transform(this.p2);
element = new AbstractPathElement3D.CurvePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.ptmp1.getX(), this.ptmp1.getY(), this.ptmp1.getZ(),
this.ptmp2.getX(), this.ptmp2.getY(), this.ptmp2.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
}
break;
case CLOSE:
this.p1.set(this.p2);
this.p2.set(this.movexProperty.get(), this.moveyProperty.get(), this.movezProperty.get());
this.transform.transform(this.p2);
element = new AbstractPathElement3D.ClosePathElement3d(
this.p1.getX(), this.p1.getY(), this.p1.getZ(),
this.p2.getX(), this.p2.getY(), this.p2.getZ());
break;
default:
}
if (element==null)
throw new NoSuchElementException();
this.iTypeProperty.set(this.iTypeProperty.get()+1);
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return Path3f.this.getWindingRule();
}
@Override
public boolean isPolyline() {
return Path3f.this.isPolyline();
}
} // class TransformPathIterator3d
/** A path iterator that is flattening the path.
* This iterator was copied from AWT FlatteningPathIterator.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
protected static class FlatteningPathIterator3f implements PathIterator3f {
/** Winding rule of the path.
*/
private final PathWindingRule windingRule;
/** The source iterator.
*/
private final PathIterator3f pathIterator;
/**
* Square of the flatness parameter for testing against squared lengths.
*/
private final double squaredFlatness;
/**
* Maximum number of recursion levels.
*/
private final int limit;
/** The recursion level at which each curve being held in storage was generated.
*/
private int levels[];
/** The cache of interpolated coords.
* Note that this must be long enough
* to store a full cubic segment and
* a relative cubic segment to avoid
* aliasing when copying the coords
* of a curve to the end of the array.
*
*/
private double hold[] = new double[36];
/** The index of the last curve segment being held for interpolation.
*/
private int holdEnd;
/**
* The index of the curve segment that was last interpolated. This
* is the curve segment ready to be returned in the next call to
* next().
*/
private int holdIndex;
/** The ending x of the last segment.
*/
private double currentX;
/** The ending y of the last segment.
*/
private double currentY;
/** The ending z of the last segment.
*/
private double currentZ;
/** The x of the last move segment.
*/
private double moveX;
/** The y of the last move segment.
*/
private double moveY;
/** The z of the last move segment.
*/
private double moveZ;
/** The index of the entry in the
* levels array of the curve segment
* at the holdIndex
*/
private int levelIndex;
/** True when iteration is done.
*/
private boolean done;
/** The type of the path element.
*/
private PathElementType holdType;
/** The x of the last move segment replied by next.
*/
private double lastNextX;
/** The y of the last move segment replied by next.
*/
private double lastNextY;
/** The y of the last move segment replied by next.
*/
private double lastNextZ;
/**
* @param windingRule1 is the winding rule of the path.
* @param pathIterator1 is the path iterator that may be used to initialize the path.
* @param flatness the maximum allowable distance between the
* control points and the flattened curve
* @param limit1 the maximum number of recursive subdivisions
* allowed for any curved segment
*/
public FlatteningPathIterator3f(PathWindingRule windingRule1, PathIterator3f pathIterator1, double flatness, int limit1) {
assert(windingRule1!=null);
assert(flatness>=0f);
assert(limit1>=0);
this.windingRule = windingRule1;
this.pathIterator = pathIterator1;
this.squaredFlatness = flatness * flatness;
this.limit = limit1;
this.levels = new int[limit1 + 1];
searchNext();
}
/**
* Ensures that the hold array can hold up to (want) more values.
* It is currently holding (hold.length - holdIndex) values.
*/
private void ensureHoldCapacity(int want) {
if (this.holdIndex - want < 0) {
int have = this.hold.length - this.holdIndex;
int newsize = this.hold.length + GROW_SIZE;
double newhold[] = new double[newsize];
System.arraycopy(this.hold, this.holdIndex,
newhold, this.holdIndex + GROW_SIZE,
have);
this.hold = newhold;
this.holdIndex += GROW_SIZE;
this.holdEnd += GROW_SIZE;
}
}
/**
* Returns the square of the flatness, or maximum distance of a
* control point from the line connecting the end points, of the
* quadratic curve specified by the control points stored in the
* indicated array at the indicated index.
* @param coords an array containing coordinate values
* @param offset the index into <code>coords</code> from which to
* to start getting the values from the array
* @return the flatness of the quadratic curve that is defined by the
* values in the specified array at the specified index.
*/
@Pure
private static double getQuadSquaredFlatness(double coords[], int offset) {
return AbstractSegment3F.distanceSquaredLinePoint(
coords[offset + 0], coords[offset + 1], coords[offset + 2],
coords[offset + 6], coords[offset + 7], coords[offset + 8],
coords[offset + 3], coords[offset + 4], coords[offset + 5]);
}
/**
* Subdivides the quadratic curve specified by the coordinates
* stored in the <code>src</code> array at indices
* <code>srcoff</code> through <code>srcoff</code> + 8
* and stores the resulting two subdivided curves into the two
* result arrays at the corresponding indices.
* Either or both of the <code>left</code> and <code>right</code>
* arrays can be <code>null</code> or a reference to the same array
* and offset as the <code>src</code> array.
* Note that the last point in the first subdivided curve is the
* same as the first point in the second subdivided curve. Thus,
* it is possible to pass the same array for <code>left</code> and
* <code>right</code> and to use offsets such that
* <code>rightoff</code> equals <code>leftoff</code> + 6 in order
* to avoid allocating extra storage for this common point.
* @param src the array holding the coordinates for the source curve
* @param srcoff the offset into the array of the beginning of the
* the 9 source coordinates
* @param left the array for storing the coordinates for the first
* half of the subdivided curve
* @param leftoff the offset into the array of the beginning of the
* the 9 left coordinates
* @param right the array for storing the coordinates for the second
* half of the subdivided curve
* @param rightoff the offset into the array of the beginning of the
* the 9 right coordinates
*/
private static void subdivideQuad(double src[], int srcoff,
double left[], int leftoff,
double right[], int rightoff) {
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double z1 = src[srcoff + 2];
double ctrlx = src[srcoff + 3];
double ctrly = src[srcoff + 4];
double ctrlz = src[srcoff + 5];
double x2 = src[srcoff + 6];
double y2 = src[srcoff + 7];
double z2 = src[srcoff + 8];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
left[leftoff + 2] = z1;
}
if (right != null) {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
right[rightoff + 8] = z2;
}
x1 = (x1 + ctrlx) / 2f;
y1 = (y1 + ctrly) / 2f;
z1 = (z1 + ctrlz) / 2f;
x2 = (x2 + ctrlx) / 2f;
y2 = (y2 + ctrly) / 2f;
z2 = (z2 + ctrlz) / 2f;
ctrlx = (x1 + x2) / 2f;
ctrly = (y1 + y2) / 2f;
ctrlz = (z1 + z2) / 2f;
if (left != null) {
left[leftoff + 3] = x1;
left[leftoff + 4] = y1;
left[leftoff + 5] = z1;
left[leftoff + 6] = ctrlx;
left[leftoff + 7] = ctrly;
left[leftoff + 8] = ctrlz;
}
if (right != null) {
right[rightoff + 0] = ctrlx;
right[rightoff + 1] = ctrly;
right[rightoff + 2] = ctrlz;
right[rightoff + 3] = x2;
right[rightoff + 4] = y2;
right[rightoff + 5] = z2;
}
}
/**
* Returns the square of the flatness of the cubic curve specified
* by the control points stored in the indicated array at the
* indicated index. The flatness is the maximum distance
* of a control point from the line connecting the end points.
* @param coords an array containing coordinates
* @param offset the index of <code>coords</code> from which to begin
* getting the end points and control points of the curve
* @return the square of the flatness of the <code>CubicCurve3D</code>
* specified by the coordinates in <code>coords</code> at
* the specified offset.
*/
@Pure
private static double getCurveSquaredFlatness(double coords[], int offset) {
return Math.max(
AbstractSegment3F.distanceSquaredSegmentPoint(
coords[offset + 9],
coords[offset + 10],
coords[offset + 11],
coords[offset + 3],
coords[offset + 4],
coords[offset + 5],
coords[offset + 0],
coords[offset + 1],
coords[offset + 2]),
AbstractSegment3F.distanceSquaredSegmentPoint(
coords[offset + 9],
coords[offset + 10],
coords[offset + 11],
coords[offset + 6],
coords[offset + 7],
coords[offset + 8],
coords[offset + 0],
coords[offset + 1],
coords[offset + 2]));
}
/**
* Subdivides the cubic curve specified by the coordinates
* stored in the <code>src</code> array at indices <code>srcoff</code>
* through (<code>srcoff</code> + 11) and stores the
* resulting two subdivided curves into the two result arrays at the
* corresponding indices.
* Either or both of the <code>left</code> and <code>right</code>
* arrays may be <code>null</code> or a reference to the same array
* as the <code>src</code> array.
* Note that the last point in the first subdivided curve is the
* same as the first point in the second subdivided curve. Thus,
* it is possible to pass the same array for <code>left</code>
* and <code>right</code> and to use offsets, such as <code>rightoff</code>
* equals (<code>leftoff</code> + 9), in order
* to avoid allocating extra storage for this common point.
* @param src the array holding the coordinates for the source curve
* @param srcoff the offset into the array of the beginning of the
* the 9 source coordinates
* @param left the array for storing the coordinates for the first
* half of the subdivided curve
* @param leftoff the offset into the array of the beginning of the
* the 9 left coordinates
* @param right the array for storing the coordinates for the second
* half of the subdivided curve
* @param rightoff the offset into the array of the beginning of the
* the 9 right coordinates
*/
private static void subdivideCurve(
double src[], int srcoff,
double left[], int leftoff,
double right[], int rightoff) {
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double z1 = src[srcoff + 2];
double ctrlx1 = src[srcoff + 3];
double ctrly1 = src[srcoff + 4];
double ctrlz1 = src[srcoff + 5];
double ctrlx2 = src[srcoff + 6];
double ctrly2 = src[srcoff + 7];
double ctrlz2 = src[srcoff + 8];
double x2 = src[srcoff + 9];
double y2 = src[srcoff + 10];
double z2 = src[srcoff + 11];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
left[leftoff + 2] = z1;
}
if (right != null) {
right[rightoff + 9] = x2;
right[rightoff + 10] = y2;
right[rightoff + 11] = z2;
}
x1 = (x1 + ctrlx1) / 2f;
y1 = (y1 + ctrly1) / 2f;
y1 = (z1 + ctrlz1) / 2f;
x2 = (x2 + ctrlx2) / 2f;
y2 = (y2 + ctrly2) / 2f;
z2 = (z2 + ctrlz2) / 2f;
double centerx = (ctrlx1 + ctrlx2) / 2f;
double centery = (ctrly1 + ctrly2) / 2f;
double centerz = (ctrlz1 + ctrlz2) / 2f;
ctrlx1 = (x1 + centerx) / 2f;
ctrly1 = (y1 + centery) / 2f;
ctrlz1 = (z1 + centerz) / 2f;
ctrlx2 = (x2 + centerx) / 2f;
ctrly2 = (y2 + centery) / 2f;
ctrlz2 = (z2 + centerz) / 2f;
centerx = (ctrlx1 + ctrlx2) / 2f;
centery = (ctrly1 + ctrly2) / 2f;
centerz = (ctrlz1 + ctrlz2) / 2f;
if (left != null) {
left[leftoff + 3] = x1;
left[leftoff + 4] = y1;
left[leftoff + 5] = z1;
left[leftoff + 6] = ctrlx1;
left[leftoff + 7] = ctrly1;
left[leftoff + 8] = ctrlz1;
left[leftoff + 9] = centerx;
left[leftoff + 10] = centery;
left[leftoff + 11] = centerz;
}
if (right != null) {
right[rightoff + 0] = centerx;
right[rightoff + 1] = centery;
right[rightoff + 2] = centerz;
right[rightoff + 3] = ctrlx2;
right[rightoff + 4] = ctrly2;
right[rightoff + 5] = ctrly2;
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
right[rightoff + 8] = z2;
}
}
private void searchNext() {
int level;
if (this.holdIndex >= this.holdEnd) {
if (!this.pathIterator.hasNext()) {
this.done = true;
return;
}
AbstractPathElement3F pathElement = this.pathIterator.next();
this.holdType = pathElement.type;
pathElement.toArray(this.hold);
this.levelIndex = 0;
this.levels[0] = 0;
}
switch (this.holdType) {
case MOVE_TO:
case LINE_TO:
this.currentX = this.hold[0];
this.currentY = this.hold[1];
this.currentZ = this.hold[2];
if (this.holdType == PathElementType.MOVE_TO) {
this.moveX = this.currentX;
this.moveY = this.currentY;
this.moveZ = this.currentZ;
}
this.holdIndex = 0;
this.holdEnd = 0;
break;
case CLOSE:
this.currentX = this.moveX;
this.currentY = this.moveY;
this.currentZ = this.moveZ;
this.holdIndex = 0;
this.holdEnd = 0;
break;
case QUAD_TO:
if (this.holdIndex >= this.holdEnd) {
// Move the coordinates to the end of the array.
this.holdIndex = this.hold.length - 9;
this.holdEnd = this.hold.length - 3;
this.hold[this.holdIndex + 0] = this.currentX;
this.hold[this.holdIndex + 1] = this.currentY;
this.hold[this.holdIndex + 2] = this.currentZ;
this.hold[this.holdIndex + 3] = this.hold[0];
this.hold[this.holdIndex + 4] = this.hold[1];
this.hold[this.holdIndex + 5] = this.hold[2];
this.hold[this.holdIndex + 6] = this.currentX = this.hold[3];
this.hold[this.holdIndex + 7] = this.currentY = this.hold[4];
this.hold[this.holdIndex + 8] = this.currentZ = this.hold[5];
}
level = this.levels[this.levelIndex];
while (level < this.limit) {
if (getQuadSquaredFlatness(this.hold, this.holdIndex) < this.squaredFlatness) {
break;
}
ensureHoldCapacity(6);
subdivideQuad(
this.hold, this.holdIndex,
this.hold, this.holdIndex - 6,
this.hold, this.holdIndex);
this.holdIndex -= 6;
// Now that we have subdivided, we have constructed
// two curves of one depth lower than the original
// curve. One of those curves is in the place of
// the former curve and one of them is in the next
// set of held coordinate slots. We now set both
// curves level values to the next higher level.
level++;
this.levels[this.levelIndex] = level;
this.levelIndex++;
this.levels[this.levelIndex] = level;
}
// This curve segment is flat enough, or it is too deep
// in recursion levels to try to flatten any more. The
// two coordinates at holdIndex+6 and holdIndex+7 now
// contain the endpoint of the curve which can be the
// endpoint of an approximating line segment.
this.holdIndex += 6;
this.levelIndex--;
break;
case CURVE_TO:
if (this.holdIndex >= this.holdEnd) {
// Move the coordinates to the end of the array.
this.holdIndex = this.hold.length - 12;
this.holdEnd = this.hold.length - 3;
this.hold[this.holdIndex + 0] = this.currentX;
this.hold[this.holdIndex + 1] = this.currentY;
this.hold[this.holdIndex + 2] = this.currentZ;
this.hold[this.holdIndex + 3] = this.hold[0];
this.hold[this.holdIndex + 4] = this.hold[1];
this.hold[this.holdIndex + 5] = this.hold[2];
this.hold[this.holdIndex + 6] = this.hold[3];
this.hold[this.holdIndex + 7] = this.hold[4];
this.hold[this.holdIndex + 8] = this.hold[5];
this.hold[this.holdIndex + 9] = this.currentX = this.hold[6];
this.hold[this.holdIndex + 10] = this.currentY = this.hold[7];
this.hold[this.holdIndex + 11] = this.currentZ = this.hold[8];
}
level = this.levels[this.levelIndex];
while (level < this.limit) {
if (getCurveSquaredFlatness(this.hold,this. holdIndex) < this.squaredFlatness) {
break;
}
ensureHoldCapacity(9);
subdivideCurve(
this.hold, this.holdIndex,
this.hold, this.holdIndex - 9,
this.hold, this.holdIndex);
this.holdIndex -= 9;
// Now that we have subdivided, we have constructed
// two curves of one depth lower than the original
// curve. One of those curves is in the place of
// the former curve and one of them is in the next
// set of held coordinate slots. We now set both
// curves level values to the next higher level.
level++;
this.levels[this.levelIndex] = level;
this.levelIndex++;
this.levels[this.levelIndex] = level;
}
// This curve segment is flat enough, or it is too deep
// in recursion levels to try to flatten any more. The
// two coordinates at holdIndex+9 and holdIndex+10 now
// contain the endpoint of the curve which can be the
// endpoint of an approximating line segment.
this.holdIndex += 9;
this.levelIndex--;
break;
default:
}
}
@Pure
@Override
public boolean hasNext() {
return !this.done;
}
@Override
public AbstractPathElement3F next() {
if (this.done) {
throw new NoSuchElementException("flattening iterator out of bounds");
}
AbstractPathElement3F element;
PathElementType type = this.holdType;
if (type!=PathElementType.CLOSE) {
double x = this.hold[this.holdIndex + 0];
double y = this.hold[this.holdIndex + 1];
double z = this.hold[this.holdIndex + 2];
if (type == PathElementType.MOVE_TO) {
element = new AbstractPathElement3F.MovePathElement3f(x, y, z);
}
else {
element = new AbstractPathElement3F.LinePathElement3f(
this.lastNextX, this.lastNextY, this.lastNextZ,
x, y, z);
}
this.lastNextX = x;
this.lastNextY = y;
this.lastNextZ = z;
}
else {
element = new AbstractPathElement3F.ClosePathElement3f(
this.lastNextX, this.lastNextY, this.lastNextZ,
this.moveX, this.moveY, this.moveZ);
this.lastNextX = this.moveX;
this.lastNextY = this.moveY;
this.lastNextZ = this.moveZ;
}
searchNext();
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return this.windingRule;
}
@Pure
@Override
public boolean isPolyline() {
return false; // Because the iterator flats the path, this is no curve inside.
}
} // class FlatteningPathIterator
/** An collection of the points of the path.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("unused")
private class PointCollection3f implements Collection<Point3D> {
/**
*/
public PointCollection3f() {
//
}
@Pure
@Override
public int size() {
return Path3f.this.size();
}
@Pure
@Override
public boolean isEmpty() {
return Path3f.this.size()<=0;
}
@Pure
@Override
public boolean contains(Object o) {
if (o instanceof Point3D) {
return Path3f.this.containsControlPoint((Point3D)o);
}
return false;
}
@Pure
@Override
public Iterator<Point3D> iterator() {
return new PointIterator3f();
}
@Pure
@Override
public Object[] toArray() {
return Path3f.this.toPointArray();
}
@Pure
@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a) {
Iterator<Point3D> iterator = new PointIterator3f();
for(int i=0; i<a.length && iterator.hasNext(); ++i) {
a[i] = (T)iterator.next();
}
return a;
}
@Override
public boolean add(Point3D e) {
if (e!=null) {
if (Path3f.this.size()==0) {
Path3f.this.moveTo(e.getX(), e.getY(), e.getZ());
}
else {
Path3f.this.lineTo(e.getX(), e.getY(), e.getZ());
}
return true;
}
return false;
}
@Override
public boolean remove(Object o) {
if (o instanceof Point3D) {
Point3D p = (Point3D)o;
return Path3f.this.remove(p.getX(), p.getY(), p.getZ());
}
return false;
}
@Pure
@Override
public boolean containsAll(Collection<?> c) {
for(Object obj : c) {
if ((!(obj instanceof Point3D))
||(!Path3f.this.containsControlPoint((Point3D)obj))) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection<? extends Point3D> c) {
boolean changed = false;
for(Point3D pts : c) {
if (add(pts)) {
changed = true;
}
}
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for(Object obj : c) {
if (obj instanceof Point3D) {
Point3D pts = (Point3D)obj;
if (Path3f.this.remove(pts.getX(), pts.getY(), pts.getZ())) {
changed = true;
}
}
}
return changed;
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
Path3f.this.clear();
}
} // class PointCollection
/** Iterator on the points of the path.
*
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class PointIterator3f implements Iterator<Point3D> {
private int index = 0;
private Point3D lastReplied = null;
/**
*/
public PointIterator3f() {
//
}
@Pure
@Override
public boolean hasNext() {
return this.index<Path3f.this.size();
}
@Override
public Point3D next() {
try {
this.lastReplied = Path3f.this.getPointAt(this.index++);
return this.lastReplied;
}
catch( Throwable e) {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
Point3D p = this.lastReplied;
this.lastReplied = null;
if (p==null)
throw new NoSuchElementException();
Path3f.this.remove(p.getX(), p.getY(), p.getZ());
}
}
}