package icy.type.geom;
import icy.type.point.Point3D;
import icy.type.rectangle.Rectangle3D;
import java.util.ArrayList;
import java.util.List;
public class Polyline3D implements Shape3D, Cloneable
{
/**
* The total number of points. The value of <code>npoints</code> represents the number of points in this
* <code>Polyline3D</code>.
*/
public int npoints;
/**
* The array of <i>x</i> coordinates. The value of {@link #npoints} is equal to the
* number of points in this <code>Polyline3D</code>.
*/
public double[] xpoints;
/**
* The array of <i>y</i> coordinates. The value of {@link #npoints} is equal to the
* number of points in this <code>Polyline3D</code>.
*/
public double[] ypoints;
/**
* The array of <i>z</i> coordinates. The value of {@link #npoints} is equal to the
* number of points in this <code>Polyline3D</code>.
*/
public double[] zpoints;
/**
* Bounds of the Polyline3D.
*
* @see #getBounds()
*/
protected Rectangle3D bounds;
protected List<Line3D> lines;
/**
* Creates an empty Polyline3D.
*/
public Polyline3D()
{
super();
reset();
}
/**
* Constructs and initializes a <code>Polyline3D</code> from the specified parameters.
*
* @param xpoints
* an array of <i>x</i> coordinates
* @param ypoints
* an array of <i>y</i> coordinates
* @param zpoints
* an array of <i>z</i> coordinates
* @param npoints
* the total number of points in the <code>Polyline3D</code>
* @exception NegativeArraySizeException
* if the value of <code>npoints</code> is negative.
* @exception IndexOutOfBoundsException
* if <code>npoints</code> is greater than the length of points array.
* @exception NullPointerException
* if one of the points array is <code>null</code>.
*/
public Polyline3D(double[] xpoints, double[] ypoints, double[] zpoints, int npoints)
{
super();
if (npoints > xpoints.length || npoints > ypoints.length || npoints > zpoints.length)
throw new IndexOutOfBoundsException("npoints > points.length");
this.npoints = npoints;
this.xpoints = new double[npoints];
this.ypoints = new double[npoints];
this.zpoints = new double[npoints];
System.arraycopy(xpoints, 0, this.xpoints, 0, npoints);
System.arraycopy(ypoints, 0, this.ypoints, 0, npoints);
System.arraycopy(zpoints, 0, this.zpoints, 0, npoints);
calculateLines();
}
/**
* Constructs and initializes a <code>Polyline3D</code> from the specified parameters.
*
* @param xpoints
* an array of <i>x</i> coordinates
* @param ypoints
* an array of <i>y</i> coordinates
* @param zpoints
* an array of <i>z</i> coordinates
* @param npoints
* the total number of points in the <code>Polyline3D</code>
* @exception NegativeArraySizeException
* if the value of <code>npoints</code> is negative.
* @exception IndexOutOfBoundsException
* if <code>npoints</code> is greater than the length of points array.
* @exception NullPointerException
* if one of the points array is <code>null</code>.
*/
public Polyline3D(int[] xpoints, int[] ypoints, int[] zpoints, int npoints)
{
super();
if (npoints > xpoints.length || npoints > ypoints.length || npoints > zpoints.length)
throw new IndexOutOfBoundsException("npoints > points.length");
this.npoints = npoints;
this.xpoints = new double[npoints];
this.ypoints = new double[npoints];
this.zpoints = new double[npoints];
for (int i = 0; i < npoints; i++)
{
this.xpoints[i] = xpoints[i];
this.ypoints[i] = ypoints[i];
this.zpoints[i] = zpoints[i];
}
calculateLines();
}
public Polyline3D(Line3D line)
{
super();
npoints = 2;
xpoints = new double[2];
ypoints = new double[2];
zpoints = new double[2];
xpoints[0] = line.getX1();
xpoints[1] = line.getX2();
ypoints[0] = line.getY1();
ypoints[1] = line.getY2();
zpoints[0] = line.getZ1();
zpoints[1] = line.getZ2();
calculateLines();
}
/**
* Resets this <code>Polyline3D</code> object to an empty polygon.
* The coordinate arrays and the data in them are left untouched
* but the number of points is reset to zero to mark the old
* vertex data as invalid and to start accumulating new vertex
* data at the beginning.
* All internally-cached data relating to the old vertices
* are discarded.
* Note that since the coordinate arrays from before the reset
* are reused, creating a new empty <code>Polyline3D</code> might
* be more memory efficient than resetting the current one if
* the number of vertices in the new polyline data is significantly
* smaller than the number of vertices in the data from before the
* reset.
*/
public void reset()
{
npoints = 0;
xpoints = new double[0];
ypoints = new double[0];
zpoints = new double[0];
bounds = new Rectangle3D.Double();
lines = new ArrayList<Line3D>();
}
@Override
public Object clone()
{
Polyline3D pol = new Polyline3D();
for (int i = 0; i < npoints; i++)
pol.addPoint(xpoints[i], ypoints[i], zpoints[i]);
return pol;
}
public void calculateLines()
{
final List<Line3D> newLines = new ArrayList<Line3D>();
double xmin, ymin, zmin;
double xmax, ymax, zmax;
if (npoints > 0)
{
// first point
Point3D pos = new Point3D.Double(xpoints[0], ypoints[0], zpoints[0]);
// init bounds
xmin = xmax = pos.getX();
ymin = ymax = pos.getY();
zmin = zmax = pos.getZ();
// special case
if (npoints == 1)
newLines.add(new Line3D(pos, pos));
else
{
for (int i = 1; i < npoints; i++)
{
final double x = xpoints[i];
final double y = ypoints[i];
final double z = zpoints[i];
final Point3D newPos = new Point3D.Double(x, y, z);
if (x < xmin)
xmin = x;
if (y < ymin)
ymin = y;
if (z < zmin)
zmin = z;
if (x > xmax)
xmax = x;
if (y > ymax)
ymax = y;
if (z > zmax)
zmax = z;
newLines.add(new Line3D(pos, newPos));
pos = newPos;
}
}
}
else
{
xmin = ymin = zmin = 0d;
xmax = ymax = zmax = 0d;
}
bounds = new Rectangle3D.Double(xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin);
lines = newLines;
}
protected void updateLines(double x, double y, double z)
{
if (lines.isEmpty())
{
lines.add(new Line3D(x, y, z, x, y, z));
bounds = new Rectangle3D.Double(x, y, z, 0d, 0d, 0d);
}
else
{
final Line3D lastLine = lines.get(lines.size() - 1);
final Line3D newLine = new Line3D(lastLine.getX2(), lastLine.getY2(), lastLine.getZ2(), x, y, z);
lines.add(newLine);
bounds.add(newLine.getBounds());
}
}
/**
* Appends the specified coordinates to this <code>Polyline3D</code>.
* <p>
* If an operation that calculates the bounding box of this <code>Polyline3D</code> has already been performed, such
* as <code>getBounds</code> or <code>contains</code>, then this method updates the bounding box.
*
* @param p
* the point to add
*/
public void addPoint(Point3D p)
{
addPoint(p.getX(), p.getY(), p.getZ());
}
/**
* Appends the specified coordinates to this <code>Polyline3D</code>.
* <p>
* If an operation that calculates the bounding box of this <code>Polyline3D</code> has already been performed, such
* as <code>getBounds</code> or <code>contains</code>, then this method updates the bounding box.
*
* @param x
* the specified x coordinate
* @param y
* the specified y coordinate
* @param z
* the specified z coordinate
*/
public void addPoint(double x, double y, double z)
{
if (npoints == xpoints.length)
{
double[] tmp;
tmp = new double[(npoints * 2) + 1];
System.arraycopy(xpoints, 0, tmp, 0, npoints);
xpoints = tmp;
tmp = new double[(npoints * 2) + 1];
System.arraycopy(ypoints, 0, tmp, 0, npoints);
ypoints = tmp;
tmp = new double[(npoints * 2) + 1];
System.arraycopy(zpoints, 0, tmp, 0, npoints);
zpoints = tmp;
}
xpoints[npoints] = x;
ypoints[npoints] = y;
zpoints[npoints] = z;
npoints++;
updateLines(x, y, z);
}
@Override
public Rectangle3D getBounds()
{
return (Rectangle3D) bounds.clone();
}
@Override
public boolean contains(Point3D p)
{
return false;
}
@Override
public boolean contains(double x, double y, double z)
{
return false;
}
@Override
public boolean intersects(double x, double y, double z, double sizeX, double sizeY, double sizeZ)
{
return intersects(new Rectangle3D.Double(x, y, z, sizeX, sizeY, sizeZ));
}
@Override
public boolean intersects(Rectangle3D r)
{
if (lines.isEmpty() || !bounds.intersects(r))
return false;
for (Line3D line : lines)
if (line.intersects(r))
return true;
return false;
}
@Override
public boolean contains(double x, double y, double z, double sizeX, double sizeY, double sizeZ)
{
return false;
}
@Override
public boolean contains(Rectangle3D r)
{
return false;
}
}