001package icy.math;
002
003import java.util.Iterator;
004
005import icy.type.geom.Line3D;
006import icy.type.point.Point3D;
007
008/**
009 * Line3D iterator (iterate over Line3D points given a wanted step).
010 * 
011 * @author Stephane Dallongeville
012 */
013public class Line3DIterator implements Iterator<Point3D>
014{
015    final static protected double DEFAULT_STEP = 1d;
016
017    protected boolean done;
018    protected int count;
019    final protected boolean forceLast;
020
021    final protected Point3D pos;
022    final protected Point3D last;
023    protected double sx, sy, sz;
024
025    /**
026     * Create the Line3D Iterator.
027     * 
028     * @param line
029     *        the lien we want to iterate points
030     * @param step
031     *        step between each point (default = 1d)
032     * @param forceLastPoint
033     *        set to <i>true</i> if you want the last point to match the end line position
034     */
035    public Line3DIterator(Line3D line, double step, boolean forceLastPoint)
036    {
037        super();
038
039        pos = line.getP1();
040        last = line.getP2();
041        done = false;
042        forceLast = forceLastPoint;
043
044        final double dx = last.getX() - pos.getX();
045        final double dy = last.getY() - pos.getY();
046        final double dz = last.getZ() - pos.getZ();
047        final double adx = Math.abs(dx);
048        final double ady = Math.abs(dy);
049        final double adz = Math.abs(dz);
050
051        final double adjStep = (step <= 0d) ? 1d : step;
052
053        // step on X axis
054        if ((adx >= ady) && (adx >= adz))
055        {
056            if (adx == 0d)
057            {
058                count = 0;
059                sy = 0;
060                sz = 0;
061            }
062            else
063            {
064                count = (int) (adx / adjStep);
065                sy = (ady / adx) * adjStep;
066                sz = (adz / adx) * adjStep;
067            }
068            sx = adjStep;
069        }
070        // adjStep on Y axis
071        else if ((ady >= adx) && (ady >= adz))
072        {
073            count = (int) (ady / adjStep);
074            sx = (adx / ady) * adjStep;
075            sy = adjStep;
076            sz = (adz / ady) * adjStep;
077        }
078        // adjStep on Z axis
079        else
080        {
081            count = (int) (adz / adjStep);
082            sx = (adx / adz) * adjStep;
083            sy = (ady / adz) * adjStep;
084            sz = adjStep;
085        }
086        // for initial position
087        count++;
088
089        // reverse step if needed
090        if (dx < 0)
091            sx = -sx;
092        if (dy < 0)
093            sy = -sy;
094        if (dz < 0)
095            sz = -sz;
096    }
097
098    /**
099     * Create the Line3D Iterator.
100     * 
101     * @param line
102     *        the lien we want to iterate points
103     * @param step
104     *        step between each point (default = 1d)
105     * @param forceLastPoint
106     *        set to <i>true</i> if you want the last point to match the end line position
107     */
108    public Line3DIterator(Line3D line, double step)
109    {
110        this(line, step, true);
111    }
112
113    /**
114     * Create the Line3D Iterator.
115     * 
116     * @param line
117     *        the lien we want to iterate points
118     */
119    public Line3DIterator(Line3D line)
120    {
121        this(line, DEFAULT_STEP, true);
122    }
123
124    @Override
125    public boolean hasNext()
126    {
127        return !done;
128    }
129
130    @Override
131    public Point3D next()
132    {
133        final Point3D result = (Point3D) pos.clone();
134
135        // done ?
136        if (--count <= 0)
137        {
138            if (forceLast)
139            {
140                // consider done only if pos is equal to last
141                done = pos.equals(last);
142                // force equality with last position
143                pos.setLocation(last);
144            }
145            else
146                done = true;
147        }
148        else
149            pos.setLocation(pos.getX() + sx, pos.getY() + sy, pos.getZ() + sz);
150
151        return result;
152    }
153
154    @Override
155    public void remove()
156    {
157        throw new UnsupportedOperationException();
158    }
159}