001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.type.point;
020
021import java.awt.Point;
022import java.awt.geom.Point2D;
023import java.util.List;
024
025/**
026 * Point3D class.<br>
027 * Incomplete implementation (work in progress...)
028 * 
029 * @author Stephane
030 */
031public abstract class Point3D implements Cloneable
032{
033    /**
034     * Returns distance between 2 Point2D using specified scale factor for x/y/z dimension.
035     */
036    public static double getDistance(Point3D pt1, Point3D pt2, double factorX, double factorY, double factorZ)
037    {
038        double px = (pt2.getX() - pt1.getX()) * factorX;
039        double py = (pt2.getY() - pt1.getY()) * factorY;
040        double pz = (pt2.getZ() - pt1.getZ()) * factorZ;
041        return Math.sqrt(px * px + py * py + pz * pz);
042    }
043
044    /**
045     * Returns total distance of the specified list of points.
046     */
047    public static double getTotalDistance(List<Point3D> points, double factorX, double factorY, double factorZ,
048            boolean connectLastPoint)
049    {
050        final int size = points.size();
051        double result = 0d;
052
053        if (size > 1)
054        {
055            for (int i = 0; i < size - 1; i++)
056                result += getDistance(points.get(i), points.get(i + 1), factorX, factorY, factorZ);
057
058            // add last to first point distance
059            if (connectLastPoint)
060                result += getDistance(points.get(size - 1), points.get(0), factorX, factorY, factorZ);
061        }
062
063        return result;
064    }
065
066    /**
067     * Cache the hash code to make computing hashes faster.
068     */
069    int hash = 0;
070
071    /**
072     * Returns the X coordinate of this <code>Point3D</code> in <code>double</code> precision.
073     * 
074     * @return the X coordinate of this <code>Point3D</code>.
075     */
076    public abstract double getX();
077
078    /**
079     * Returns the Y coordinate of this <code>Point3D</code> in <code>double</code> precision.
080     * 
081     * @return the Y coordinate of this <code>Point3D</code>.
082     */
083    public abstract double getY();
084
085    /**
086     * Returns the Z coordinate of this <code>Point3D</code> in <code>double</code> precision.
087     * 
088     * @return the Z coordinate of this <code>Point3D</code>.
089     */
090    public abstract double getZ();
091
092    /**
093     * Sets the X coordinate of this <code>Point3D</code> in <code>double</code> precision.
094     */
095    public abstract void setX(double x);
096
097    /**
098     * Sets the Y coordinate of this <code>Point3D</code> in <code>double</code> precision.
099     */
100    public abstract void setY(double y);
101
102    /**
103     * Sets the Z coordinate of this <code>Point3D</code> in <code>double</code> precision.
104     */
105    public abstract void setZ(double z);
106
107    /**
108     * Sets the location of this <code>Point3D</code> to the
109     * specified <code>double</code> coordinates.
110     * 
111     * @param x
112     *        the new X coordinate of this {@code Point3D}
113     * @param y
114     *        the new Y coordinate of this {@code Point3D}
115     * @param z
116     *        the new Z coordinate of this {@code Point3D}
117     */
118    public void setLocation(double x, double y, double z)
119    {
120        setX(x);
121        setY(y);
122        setZ(z);
123    }
124
125    /**
126     * Sets the location of this <code>Point3D</code> to the same
127     * coordinates as the specified <code>Point3D</code> object.
128     * 
129     * @param p
130     *        the specified <code>Point3D</code> to which to set
131     *        this <code>Point3D</code>
132     */
133    public void setLocation(Point3D p)
134    {
135        setLocation(p.getX(), p.getY(), p.getZ());
136    }
137
138    /**
139     * Translate this <code>Point3D</code> by the specified <code>double</code> coordinates.
140     * 
141     * @param x
142     *        the X translation value
143     * @param y
144     *        the Y translation value
145     * @param z
146     *        the Z translation value
147     */
148    public void translate(double x, double y, double z)
149    {
150        setX(getX() + x);
151        setY(getY() + y);
152        setZ(getZ() + z);
153    }
154
155    /**
156     * Translate this <code>Point3D</code> by the specified <code>Point3D</code> coordinates.
157     * 
158     * @param p
159     *        the specified <code>Point3D</code> used to translate this <code>Point3D</code>
160     */
161    public void translate(Point3D p)
162    {
163        translate(p.getX(), p.getY(), p.getZ());
164    }
165
166    /**
167     * Convert to 2D point
168     */
169    public abstract Point2D toPoint2D();
170
171    /**
172     * Computes the distance between this point and point {@code (x1, y1, z1)}.
173     *
174     * @param x1
175     *        the x coordinate of other point
176     * @param y1
177     *        the y coordinate of other point
178     * @param z1
179     *        the z coordinate of other point
180     * @return the distance between this point and point {@code (x1, y1, z1)}.
181     */
182    public double distance(double x1, double y1, double z1)
183    {
184        double a = getX() - x1;
185        double b = getY() - y1;
186        double c = getZ() - z1;
187        return Math.sqrt(a * a + b * b + c * c);
188    }
189
190    /**
191     * Computes the distance between this point and the specified {@code point}.
192     *
193     * @param point
194     *        the other point
195     * @return the distance between this point and the specified {@code point}.
196     * @throws NullPointerException
197     *         if the specified {@code point} is null
198     */
199    public double distance(Point3D point)
200    {
201        return distance(point.getX(), point.getY(), point.getZ());
202    }
203
204    /**
205     * Normalizes the relative magnitude vector represented by this instance.
206     * Returns a vector with the same direction and magnitude equal to 1.
207     * If this is a zero vector, a zero vector is returned.
208     * 
209     * @return the normalized vector represented by a {@code Point3D} instance
210     */
211    public Point3D normalize()
212    {
213        final double mag = magnitude();
214
215        if (mag == 0d)
216            return new Point3D.Double(0d, 0d, 0d);
217
218        return new Point3D.Double(getX() / mag, getY() / mag, getZ() / mag);
219    }
220
221    /**
222     * Returns a point which lies in the middle between this point and the
223     * specified coordinates.
224     * 
225     * @param x
226     *        the X coordinate of the second end point
227     * @param y
228     *        the Y coordinate of the second end point
229     * @param z
230     *        the Z coordinate of the second end point
231     * @return the point in the middle
232     */
233    public Point3D midpoint(double x, double y, double z)
234    {
235        return new Point3D.Double(x + (getX() - x) / 2d, y + (getY() - y) / 2d, z + (getZ() - z) / 2d);
236    }
237
238    /**
239     * Returns a point which lies in the middle between this point and the
240     * specified point.
241     * 
242     * @param point
243     *        the other end point
244     * @return the point in the middle
245     * @throws NullPointerException
246     *         if the specified {@code point} is null
247     */
248    public Point3D midpoint(Point3D point)
249    {
250        return midpoint(point.getX(), point.getY(), point.getZ());
251    }
252
253    /**
254     * Computes the angle (in degrees) between the vector represented
255     * by this point and the specified vector.
256     * 
257     * @param x
258     *        the X magnitude of the other vector
259     * @param y
260     *        the Y magnitude of the other vector
261     * @param z
262     *        the Z magnitude of the other vector
263     * @return the angle between the two vectors measured in degrees
264     */
265    public double angle(double x, double y, double z)
266    {
267        final double ax = getX();
268        final double ay = getY();
269        final double az = getZ();
270
271        final double delta = (ax * x + ay * y + az * z)
272                / Math.sqrt((ax * ax + ay * ay + az * az) * (x * x + y * y + z * z));
273
274        if (delta > 1d)
275            return 0d;
276        if (delta < -1d)
277            return 180d;
278
279        return Math.toDegrees(Math.acos(delta));
280    }
281
282    /**
283     * Computes the angle (in degrees) between the vector represented
284     * by this point and the vector represented by the specified point.
285     * 
286     * @param vector
287     *        the other vector
288     * @return the angle between the two vectors measured in degrees, {@code NaN} if any of the two vectors is a zero
289     *         vector
290     * @throws NullPointerException
291     *         if the specified {@code vector} is null
292     */
293    public double angle(Point3D vector)
294    {
295        return angle(vector.getX(), vector.getY(), vector.getZ());
296    }
297
298    /**
299     * Computes the angle (in degrees) between the three points with this point
300     * as a vertex.
301     * 
302     * @param p1
303     *        one point
304     * @param p2
305     *        other point
306     * @return angle between the vectors (this, p1) and (this, p2) measured
307     *         in degrees, {@code NaN} if the three points are not different
308     *         from one another
309     * @throws NullPointerException
310     *         if the {@code p1} or {@code p2} is null
311     */
312    public double angle(Point3D p1, Point3D p2)
313    {
314        final double x = getX();
315        final double y = getY();
316        final double z = getZ();
317
318        final double ax = p1.getX() - x;
319        final double ay = p1.getY() - y;
320        final double az = p1.getZ() - z;
321        final double bx = p2.getX() - x;
322        final double by = p2.getY() - y;
323        final double bz = p2.getZ() - z;
324
325        final double delta = (ax * bx + ay * by + az * bz)
326                / Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz));
327
328        if (delta > 1.0)
329            return 0.0;
330
331        if (delta < -1.0)
332            return 180.0;
333
334        return Math.toDegrees(Math.acos(delta));
335    }
336
337    /**
338     * Computes norm2 (square length) of the vector represented by this Point3D.
339     * 
340     * @return norm2 length of the vector
341     */
342    public double norm2()
343    {
344        final double x = getX();
345        final double y = getY();
346        final double z = getZ();
347
348        return x * x + y * y + z * z;
349    }
350
351    /**
352     * Computes length of the vector represented by this Point3D.
353     * 
354     * @return length of the vector
355     */
356    public double length()
357    {
358        return Math.sqrt(norm2());
359    }
360
361    /**
362     * Same as {@link #length()}
363     */
364    public double magnitude()
365    {
366        return length();
367    }
368
369    /**
370     * Computes dot (scalar) product of the vector represented by this instance
371     * and the specified vector.
372     * 
373     * @param x
374     *        the X magnitude of the other vector
375     * @param y
376     *        the Y magnitude of the other vector
377     * @param z
378     *        the Z magnitude of the other vector
379     * @return the dot product of the two vectors
380     */
381    public double dotProduct(double x, double y, double z)
382    {
383        return getX() * x + getY() * y + getZ() * z;
384    }
385
386    /**
387     * Computes dot (scalar) product of the vector represented by this instance
388     * and the specified vector.
389     * 
390     * @param vector
391     *        the other vector
392     * @return the dot product of the two vectors
393     * @throws NullPointerException
394     *         if the specified {@code vector} is null
395     */
396    public double dotProduct(Point3D vector)
397    {
398        return dotProduct(vector.getX(), vector.getY(), vector.getZ());
399    }
400
401    /**
402     * Computes cross product of the vector represented by this instance
403     * and the specified vector.
404     * 
405     * @param x
406     *        the X magnitude of the other vector
407     * @param y
408     *        the Y magnitude of the other vector
409     * @param z
410     *        the Z magnitude of the other vector
411     * @return the cross product of the two vectors
412     */
413    public Point3D crossProduct(double x, double y, double z)
414    {
415        final double ax = getX();
416        final double ay = getY();
417        final double az = getZ();
418
419        return new Point3D.Double(ay * z - az * y, az * x - ax * z, ax * y - ay * x);
420    }
421
422    /**
423     * Computes cross product of the vector represented by this instance
424     * and the specified vector.
425     * 
426     * @param vector
427     *        the other vector
428     * @return the cross product of the two vectors
429     * @throws NullPointerException
430     *         if the specified {@code vector} is null
431     */
432    public Point3D crossProduct(Point3D vector)
433    {
434        return crossProduct(vector.getX(), vector.getY(), vector.getZ());
435    }
436
437    @Override
438    public boolean equals(Object obj)
439    {
440        if (obj == this)
441            return true;
442
443        if (obj instanceof Point3D)
444        {
445            final Point3D pt = (Point3D) obj;
446            return (getX() == pt.getX()) && (getY() == pt.getY()) && (getZ() == pt.getZ());
447        }
448
449        return super.equals(obj);
450    }
451
452    /**
453     * Creates a new object of the same class as this object.
454     * 
455     * @return a clone of this instance.
456     * @exception OutOfMemoryError
457     *            if there is not enough memory.
458     * @see java.lang.Cloneable
459     */
460    @Override
461    public Object clone()
462    {
463        try
464        {
465            return super.clone();
466        }
467        catch (CloneNotSupportedException e)
468        {
469            // this shouldn't happen, since we are Cloneable
470            throw new InternalError();
471        }
472    }
473
474    /**
475     * @return a hash code for this {@code Point3D} object.
476     */
477    @Override
478    public int hashCode()
479    {
480        if (hash == 0)
481        {
482            long bits = 7L;
483            bits = 31L * bits + java.lang.Double.doubleToLongBits(getX());
484            bits = 31L * bits + java.lang.Double.doubleToLongBits(getY());
485            bits = 31L * bits + java.lang.Double.doubleToLongBits(getZ());
486            hash = (int) (bits ^ (bits >> 32));
487            // so hash could never be 0 when computed
488            hash |= 1;
489        }
490
491        return hash;
492    }
493
494    /**
495     * Returns a string representation of this {@code Point3D}.
496     * This method is intended to be used only for informational purposes.
497     */
498    @Override
499    public String toString()
500    {
501        return getClass().getName() + "[" + getX() + "," + getY() + "," + getZ() + "]";
502    }
503
504    public static class Double extends Point3D
505    {
506        /**
507         * Create an array of Point3D.Double from the input double array.<br>
508         * <br>
509         * The format of the input array should be as follow:<br>
510         * <code>input.lenght</code> = number of point * 3.<br>
511         * <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
512         * <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
513         * <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
514         */
515        public static Point3D.Double[] toPoint3D(double[] input)
516        {
517            final Point3D.Double[] result = new Point3D.Double[input.length / 3];
518
519            int pt = 0;
520            for (int i = 0; i < input.length; i += 3)
521                result[pt++] = new Point3D.Double(input[i + 0], input[i + 1], input[i + 2]);
522
523            return result;
524        }
525
526        /**
527         * Create an array of double from the input Point3D.Double array.<br>
528         * <br>
529         * The format of the output array is as follow:<br>
530         * <code>result.lenght</code> = number of point * 3.<br>
531         * <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
532         * <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
533         * <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
534         */
535        public static double[] toDoubleArray(Point3D.Double[] input)
536        {
537            final double[] result = new double[input.length * 3];
538
539            int off = 0;
540            for (Point3D.Double pt : input)
541            {
542                result[off++] = pt.x;
543                result[off++] = pt.y;
544                result[off++] = pt.z;
545            }
546
547            return result;
548        }
549
550        public double x;
551        public double y;
552        public double z;
553
554        public Double(double x, double y, double z)
555        {
556            super();
557
558            this.x = x;
559            this.y = y;
560            this.z = z;
561        }
562
563        public Double(double[] xyz)
564        {
565            final int len = xyz.length;
566
567            if (len > 0)
568                this.x = xyz[0];
569            if (len > 1)
570                this.y = xyz[1];
571            if (len > 2)
572                this.z = xyz[2];
573        }
574
575        public Double()
576        {
577            this(0, 0, 0);
578        }
579
580        @Override
581        public double getX()
582        {
583            return x;
584        }
585
586        @Override
587        public void setX(double x)
588        {
589            this.x = x;
590            hash = 0;
591        }
592
593        @Override
594        public double getY()
595        {
596            return y;
597        }
598
599        @Override
600        public void setY(double y)
601        {
602            this.y = y;
603            hash = 0;
604        }
605
606        @Override
607        public double getZ()
608        {
609            return z;
610        }
611
612        @Override
613        public void setZ(double z)
614        {
615            this.z = z;
616            hash = 0;
617        }
618
619        @Override
620        public String toString()
621        {
622            return "Point3D.Double[" + x + "," + y + "," + z + "]";
623        }
624
625        @Override
626        public Point2D toPoint2D()
627        {
628            return new Point2D.Double(x, y);
629        }
630    }
631
632    public static class Float extends Point3D
633    {
634        /**
635         * Create an array of Point3D.Float from the input float array.<br>
636         * <br>
637         * The format of the input array should be as follow:<br>
638         * <code>input.lenght</code> = number of point * 3.<br>
639         * <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
640         * <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
641         * <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
642         */
643        public static Point3D.Float[] toPoint3D(float[] input)
644        {
645            final Point3D.Float[] result = new Point3D.Float[input.length / 3];
646
647            int pt = 0;
648            for (int i = 0; i < input.length; i += 3)
649                result[pt++] = new Point3D.Float(input[i + 0], input[i + 1], input[i + 2]);
650
651            return result;
652        }
653
654        /**
655         * Create an array of float from the input Point3D.Float array.<br>
656         * <br>
657         * The format of the output array is as follow:<br>
658         * <code>result.lenght</code> = number of point * 3.<br>
659         * <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
660         * <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
661         * <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
662         */
663        public static float[] toFloatArray(Point3D.Float[] input)
664        {
665            final float[] result = new float[input.length * 3];
666
667            int off = 0;
668            for (Point3D.Float pt : input)
669            {
670                result[off++] = pt.x;
671                result[off++] = pt.y;
672                result[off++] = pt.z;
673            }
674
675            return result;
676        }
677
678        public float x;
679        public float y;
680        public float z;
681
682        public Float(float x, float y, float z)
683        {
684            super();
685
686            this.x = x;
687            this.y = y;
688            this.z = z;
689        }
690
691        public Float(float[] xyz)
692        {
693            final int len = xyz.length;
694
695            if (len > 0)
696                this.x = xyz[0];
697            if (len > 1)
698                this.y = xyz[1];
699            if (len > 2)
700                this.z = xyz[2];
701        }
702
703        public Float()
704        {
705            this(0, 0, 0);
706        }
707
708        @Override
709        public double getX()
710        {
711            return x;
712        }
713
714        @Override
715        public void setX(double x)
716        {
717            this.x = (float) x;
718            hash = 0;
719        }
720
721        @Override
722        public double getY()
723        {
724            return y;
725        }
726
727        @Override
728        public void setY(double y)
729        {
730            this.y = (float) y;
731            hash = 0;
732        }
733
734        @Override
735        public double getZ()
736        {
737            return z;
738        }
739
740        @Override
741        public void setZ(double z)
742        {
743            this.z = (float) z;
744            hash = 0;
745        }
746
747        @Override
748        public String toString()
749        {
750            return "Point3D.Float[" + x + "," + y + "," + z + "]";
751        }
752
753        @Override
754        public Point2D toPoint2D()
755        {
756            return new Point2D.Float(x, y);
757        }
758    }
759
760    public static class Integer extends Point3D
761    {
762        /**
763         * Create an array of Point3D.Integer from the input integer array.<br>
764         * <br>
765         * The format of the input array should be as follow:<br>
766         * <code>input.lenght</code> = number of point * 3.<br>
767         * <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
768         * <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
769         * <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
770         */
771        public static Point3D.Integer[] toPoint3D(int[] input)
772        {
773            final Point3D.Integer[] result = new Point3D.Integer[input.length / 3];
774
775            int pt = 0;
776            for (int i = 0; i < input.length; i += 3)
777                result[pt++] = new Point3D.Integer(input[i + 0], input[i + 1], input[i + 2]);
778
779            return result;
780        }
781
782        /**
783         * Create an array of integer from the input Point3D.Integer array.<br>
784         * <br>
785         * The format of the output array is as follow:<br>
786         * <code>result.lenght</code> = number of point * 3.<br>
787         * <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
788         * <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
789         * <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
790         */
791        public static int[] toIntegerArray(Point3D.Integer[] input)
792        {
793            final int[] result = new int[input.length * 3];
794
795            int off = 0;
796            for (Point3D.Integer pt : input)
797            {
798                result[off++] = pt.x;
799                result[off++] = pt.y;
800                result[off++] = pt.z;
801            }
802
803            return result;
804        }
805
806        public int x;
807        public int y;
808        public int z;
809
810        public Integer(int x, int y, int z)
811        {
812            super();
813
814            this.x = x;
815            this.y = y;
816            this.z = z;
817        }
818
819        public Integer(int[] xyz)
820        {
821            final int len = xyz.length;
822
823            if (len > 0)
824                this.x = xyz[0];
825            if (len > 1)
826                this.y = xyz[1];
827            if (len > 2)
828                this.z = xyz[2];
829        }
830
831        public Integer()
832        {
833            this(0, 0, 0);
834        }
835
836        @Override
837        public double getX()
838        {
839            return x;
840        }
841
842        @Override
843        public void setX(double x)
844        {
845            this.x = (int) x;
846            hash = 0;
847        }
848
849        @Override
850        public double getY()
851        {
852            return y;
853        }
854
855        @Override
856        public void setY(double y)
857        {
858            this.y = (int) y;
859            hash = 0;
860        }
861
862        @Override
863        public double getZ()
864        {
865            return z;
866        }
867
868        @Override
869        public void setZ(double z)
870        {
871            this.z = (int) z;
872            hash = 0;
873        }
874
875        @Override
876        public String toString()
877        {
878            return "Point3D.Integer[" + x + "," + y + "," + z + "]";
879        }
880
881        @Override
882        public Point2D toPoint2D()
883        {
884            return new Point(x, y);
885        }
886    }
887}