001package icy.type.geom;
002
003import icy.type.point.Point3D;
004import icy.type.rectangle.Rectangle3D;
005
006import java.util.ArrayList;
007import java.util.List;
008
009public class Polyline3D implements Shape3D, Cloneable
010{
011    /**
012     * The total number of points. The value of <code>npoints</code> represents the number of points in this
013     * <code>Polyline3D</code>.
014     */
015    public int npoints;
016
017    /**
018     * The array of <i>x</i> coordinates. The value of {@link #npoints} is equal to the
019     * number of points in this <code>Polyline3D</code>.
020     */
021    public double[] xpoints;
022
023    /**
024     * The array of <i>y</i> coordinates. The value of {@link #npoints} is equal to the
025     * number of points in this <code>Polyline3D</code>.
026     */
027    public double[] ypoints;
028    /**
029     * The array of <i>z</i> coordinates. The value of {@link #npoints} is equal to the
030     * number of points in this <code>Polyline3D</code>.
031     */
032    public double[] zpoints;
033
034    /**
035     * Bounds of the Polyline3D.
036     * 
037     * @see #getBounds()
038     */
039    protected Rectangle3D bounds;
040
041    protected List<Line3D> lines;
042
043    /**
044     * Creates an empty Polyline3D.
045     */
046    public Polyline3D()
047    {
048        super();
049
050        reset();
051    }
052
053    /**
054     * Constructs and initializes a <code>Polyline3D</code> from the specified parameters.
055     * 
056     * @param xpoints
057     *        an array of <i>x</i> coordinates
058     * @param ypoints
059     *        an array of <i>y</i> coordinates
060     * @param zpoints
061     *        an array of <i>z</i> coordinates
062     * @param npoints
063     *        the total number of points in the <code>Polyline3D</code>
064     * @exception NegativeArraySizeException
065     *            if the value of <code>npoints</code> is negative.
066     * @exception IndexOutOfBoundsException
067     *            if <code>npoints</code> is greater than the length of points array.
068     * @exception NullPointerException
069     *            if one of the points array is <code>null</code>.
070     */
071    public Polyline3D(double[] xpoints, double[] ypoints, double[] zpoints, int npoints)
072    {
073        super();
074
075        if (npoints > xpoints.length || npoints > ypoints.length || npoints > zpoints.length)
076            throw new IndexOutOfBoundsException("npoints > points.length");
077
078        this.npoints = npoints;
079        this.xpoints = new double[npoints];
080        this.ypoints = new double[npoints];
081        this.zpoints = new double[npoints];
082
083        System.arraycopy(xpoints, 0, this.xpoints, 0, npoints);
084        System.arraycopy(ypoints, 0, this.ypoints, 0, npoints);
085        System.arraycopy(zpoints, 0, this.zpoints, 0, npoints);
086
087        calculateLines();
088    }
089
090    /**
091     * Constructs and initializes a <code>Polyline3D</code> from the specified parameters.
092     * 
093     * @param xpoints
094     *        an array of <i>x</i> coordinates
095     * @param ypoints
096     *        an array of <i>y</i> coordinates
097     * @param zpoints
098     *        an array of <i>z</i> coordinates
099     * @param npoints
100     *        the total number of points in the <code>Polyline3D</code>
101     * @exception NegativeArraySizeException
102     *            if the value of <code>npoints</code> is negative.
103     * @exception IndexOutOfBoundsException
104     *            if <code>npoints</code> is greater than the length of points array.
105     * @exception NullPointerException
106     *            if one of the points array is <code>null</code>.
107     */
108    public Polyline3D(int[] xpoints, int[] ypoints, int[] zpoints, int npoints)
109    {
110        super();
111
112        if (npoints > xpoints.length || npoints > ypoints.length || npoints > zpoints.length)
113            throw new IndexOutOfBoundsException("npoints > points.length");
114
115        this.npoints = npoints;
116        this.xpoints = new double[npoints];
117        this.ypoints = new double[npoints];
118        this.zpoints = new double[npoints];
119
120        for (int i = 0; i < npoints; i++)
121        {
122            this.xpoints[i] = xpoints[i];
123            this.ypoints[i] = ypoints[i];
124            this.zpoints[i] = zpoints[i];
125        }
126
127        calculateLines();
128    }
129
130    public Polyline3D(Line3D line)
131    {
132        super();
133
134        npoints = 2;
135        xpoints = new double[2];
136        ypoints = new double[2];
137        zpoints = new double[2];
138
139        xpoints[0] = line.getX1();
140        xpoints[1] = line.getX2();
141        ypoints[0] = line.getY1();
142        ypoints[1] = line.getY2();
143        zpoints[0] = line.getZ1();
144        zpoints[1] = line.getZ2();
145
146        calculateLines();
147    }
148
149    /**
150     * Resets this <code>Polyline3D</code> object to an empty polygon.
151     * The coordinate arrays and the data in them are left untouched
152     * but the number of points is reset to zero to mark the old
153     * vertex data as invalid and to start accumulating new vertex
154     * data at the beginning.
155     * All internally-cached data relating to the old vertices
156     * are discarded.
157     * Note that since the coordinate arrays from before the reset
158     * are reused, creating a new empty <code>Polyline3D</code> might
159     * be more memory efficient than resetting the current one if
160     * the number of vertices in the new polyline data is significantly
161     * smaller than the number of vertices in the data from before the
162     * reset.
163     */
164    public void reset()
165    {
166        npoints = 0;
167        xpoints = new double[0];
168        ypoints = new double[0];
169        zpoints = new double[0];
170        bounds = new Rectangle3D.Double();
171        lines = new ArrayList<Line3D>();
172    }
173
174    @Override
175    public Object clone()
176    {
177        Polyline3D pol = new Polyline3D();
178
179        for (int i = 0; i < npoints; i++)
180            pol.addPoint(xpoints[i], ypoints[i], zpoints[i]);
181
182        return pol;
183    }
184
185    public void calculateLines()
186    {
187        final List<Line3D> newLines = new ArrayList<Line3D>();
188        double xmin, ymin, zmin;
189        double xmax, ymax, zmax;
190
191        if (npoints > 0)
192        {
193            // first point
194            Point3D pos = new Point3D.Double(xpoints[0], ypoints[0], zpoints[0]);
195
196            // init bounds
197            xmin = xmax = pos.getX();
198            ymin = ymax = pos.getY();
199            zmin = zmax = pos.getZ();
200
201            // special case
202            if (npoints == 1)
203                newLines.add(new Line3D(pos, pos));
204            else
205            {
206                for (int i = 1; i < npoints; i++)
207                {
208                    final double x = xpoints[i];
209                    final double y = ypoints[i];
210                    final double z = zpoints[i];
211                    final Point3D newPos = new Point3D.Double(x, y, z);
212
213                    if (x < xmin)
214                        xmin = x;
215                    if (y < ymin)
216                        ymin = y;
217                    if (z < zmin)
218                        zmin = z;
219                    if (x > xmax)
220                        xmax = x;
221                    if (y > ymax)
222                        ymax = y;
223                    if (z > zmax)
224                        zmax = z;
225
226                    newLines.add(new Line3D(pos, newPos));
227                    pos = newPos;
228                }
229            }
230        }
231        else
232        {
233            xmin = ymin = zmin = 0d;
234            xmax = ymax = zmax = 0d;
235        }
236
237        bounds = new Rectangle3D.Double(xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin);
238        lines = newLines;
239    }
240
241    protected void updateLines(double x, double y, double z)
242    {
243        if (lines.isEmpty())
244        {
245            lines.add(new Line3D(x, y, z, x, y, z));
246            bounds = new Rectangle3D.Double(x, y, z, 0d, 0d, 0d);
247        }
248        else
249        {
250            final Line3D lastLine = lines.get(lines.size() - 1);
251            final Line3D newLine = new Line3D(lastLine.getX2(), lastLine.getY2(), lastLine.getZ2(), x, y, z);
252            lines.add(newLine);
253            bounds.add(newLine.getBounds());
254        }
255    }
256
257    /**
258     * Appends the specified coordinates to this <code>Polyline3D</code>.
259     * <p>
260     * If an operation that calculates the bounding box of this <code>Polyline3D</code> has already been performed, such
261     * as <code>getBounds</code> or <code>contains</code>, then this method updates the bounding box.
262     * 
263     * @param p
264     *        the point to add
265     */
266    public void addPoint(Point3D p)
267    {
268        addPoint(p.getX(), p.getY(), p.getZ());
269    }
270
271    /**
272     * Appends the specified coordinates to this <code>Polyline3D</code>.
273     * <p>
274     * If an operation that calculates the bounding box of this <code>Polyline3D</code> has already been performed, such
275     * as <code>getBounds</code> or <code>contains</code>, then this method updates the bounding box.
276     * 
277     * @param x
278     *        the specified x coordinate
279     * @param y
280     *        the specified y coordinate
281     * @param z
282     *        the specified z coordinate
283     */
284    public void addPoint(double x, double y, double z)
285    {
286        if (npoints == xpoints.length)
287        {
288            double[] tmp;
289
290            tmp = new double[(npoints * 2) + 1];
291            System.arraycopy(xpoints, 0, tmp, 0, npoints);
292            xpoints = tmp;
293
294            tmp = new double[(npoints * 2) + 1];
295            System.arraycopy(ypoints, 0, tmp, 0, npoints);
296            ypoints = tmp;
297
298            tmp = new double[(npoints * 2) + 1];
299            System.arraycopy(zpoints, 0, tmp, 0, npoints);
300            zpoints = tmp;
301        }
302
303        xpoints[npoints] = x;
304        ypoints[npoints] = y;
305        zpoints[npoints] = z;
306        npoints++;
307
308        updateLines(x, y, z);
309    }
310
311    @Override
312    public Rectangle3D getBounds()
313    {
314        return (Rectangle3D) bounds.clone();
315    }
316
317    @Override
318    public boolean contains(Point3D p)
319    {
320        return false;
321    }
322
323    @Override
324    public boolean contains(double x, double y, double z)
325    {
326        return false;
327    }
328
329    @Override
330    public boolean intersects(double x, double y, double z, double sizeX, double sizeY, double sizeZ)
331    {
332        return intersects(new Rectangle3D.Double(x, y, z, sizeX, sizeY, sizeZ));
333    }
334
335    @Override
336    public boolean intersects(Rectangle3D r)
337    {
338        if (lines.isEmpty() || !bounds.intersects(r))
339            return false;
340
341        for (Line3D line : lines)
342            if (line.intersects(r))
343                return true;
344
345        return false;
346    }
347
348    @Override
349    public boolean contains(double x, double y, double z, double sizeX, double sizeY, double sizeZ)
350    {
351        return false;
352    }
353
354    @Override
355    public boolean contains(Rectangle3D r)
356    {
357        return false;
358    }
359}