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; } }