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}