1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 package com.javafx.experiments.utils3d.geom.transform;
  34 
  35 import com.javafx.experiments.utils3d.geom.*;
  36 
  37 /**
  38  *
  39  */
  40 public class Translate2D extends BaseTransform {
  41     private double mxt;
  42     private double myt;
  43 
  44     public static BaseTransform getInstance(double mxt, double myt) {
  45         if (mxt == 0.0 && myt == 0.0) {
  46             return IDENTITY_TRANSFORM;
  47         } else {
  48             return new Translate2D(mxt, myt);
  49         }
  50     }
  51 
  52     public Translate2D(double tx, double ty) {
  53         this.mxt = tx;
  54         this.myt = ty;
  55     }
  56 
  57     public Translate2D(BaseTransform tx) {
  58         if (!tx.isTranslateOrIdentity()) {
  59             degreeError(Degree.TRANSLATE_2D);
  60         }
  61         this.mxt = tx.getMxt();
  62         this.myt = tx.getMyt();
  63     }
  64 
  65     @Override
  66     public Degree getDegree() {
  67         return Degree.TRANSLATE_2D;
  68     }
  69 
  70     @Override
  71     public double getDeterminant() {
  72         return 1.0;
  73     }
  74 
  75     @Override
  76     public double getMxt() {
  77         return mxt;
  78     }
  79 
  80     @Override
  81     public double getMyt() {
  82         return myt;
  83     }
  84 
  85     @Override
  86     public int getType() {
  87         return (mxt == 0.0 && myt == 0.0) ? TYPE_IDENTITY : TYPE_TRANSLATION;
  88     }
  89 
  90     @Override
  91     public boolean isIdentity() {
  92         return (mxt == 0.0 && myt == 0.0);
  93     }
  94 
  95     @Override
  96     public boolean isTranslateOrIdentity() {
  97         return true;
  98     }
  99 
 100     @Override
 101     public boolean is2D() {
 102         return true;
 103     }
 104 
 105     @Override
 106     public Point2D transform(Point2D src, Point2D dst) {
 107         if (dst == null) dst = makePoint(src, dst);
 108         dst.setLocation(
 109             (float) (src.x + mxt),
 110             (float) (src.y + myt));
 111         return dst;
 112     }
 113 
 114     @Override
 115     public Point2D inverseTransform(Point2D src, Point2D dst) {
 116         if (dst == null) dst = makePoint(src, dst);
 117         dst.setLocation(
 118             (float) (src.x - mxt),
 119             (float) (src.y - myt));
 120         return dst;
 121     }
 122 
 123     @Override
 124     public Vec3d transform(Vec3d src, Vec3d dst) {
 125         if (dst == null) {
 126             dst = new Vec3d();
 127         }
 128         dst.x = src.x + mxt;
 129         dst.y = src.y + myt;
 130         dst.z = src.z;
 131         return dst;
 132     }
 133 
 134     @Override
 135     public Vec3d deltaTransform(Vec3d src, Vec3d dst) {
 136         if (dst == null) {
 137             dst = new Vec3d();
 138         }
 139         dst.set(src);
 140         return dst;
 141     }
 142 
 143     @Override
 144     public Vec3d inverseTransform(Vec3d src, Vec3d dst) {
 145         if (dst == null) {
 146             dst = new Vec3d();
 147         }
 148         dst.x = src.x - mxt;
 149         dst.y = src.y - myt;
 150         dst.z = src.z;
 151         return dst;
 152     }
 153 
 154     @Override
 155     public Vec3d inverseDeltaTransform(Vec3d src, Vec3d dst) {
 156         if (dst == null) {
 157             dst = new Vec3d();
 158         }
 159         dst.set(src);
 160         return dst;
 161     }
 162 
 163     @Override
 164     public void transform(float[] srcPts, int srcOff,
 165                           float[] dstPts, int dstOff,
 166                           int numPts)
 167     {
 168         float tx = (float) this.mxt;
 169         float ty = (float) this.myt;
 170         if (dstPts == srcPts) {
 171             if (dstOff > srcOff && dstOff < srcOff + numPts * 2) {
 172                 // If the arrays overlap partially with the destination higher
 173                 // than the source and we transform the coordinates normally
 174                 // we would overwrite some of the later source coordinates
 175                 // with results of previous transformations.
 176                 // To get around this we use arraycopy to copy the points
 177                 // to their final destination with correct overwrite
 178                 // handling and then transform them in place in the new
 179                 // safer location.
 180                 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 181                 // srcPts = dstPts;     // They are known to be equal.
 182                 srcOff = dstOff;
 183             }
 184             if (dstOff == srcOff && tx == 0.0f && ty == 0.0f) {
 185                 return;
 186             }
 187         }
 188         for (int i = 0; i < numPts; i++) {
 189             dstPts[dstOff++] = srcPts[srcOff++] + tx;
 190             dstPts[dstOff++] = srcPts[srcOff++] + ty;
 191         }
 192     }
 193 
 194     @Override
 195     public void transform(double[] srcPts, int srcOff,
 196                           double[] dstPts, int dstOff,
 197                           int numPts)
 198     {
 199         double tx = this.mxt;
 200         double ty = this.myt;
 201         if (dstPts == srcPts) {
 202             if (dstOff > srcOff && dstOff < srcOff + numPts * 2) {
 203                 // If the arrays overlap partially with the destination higher
 204                 // than the source and we transform the coordinates normally
 205                 // we would overwrite some of the later source coordinates
 206                 // with results of previous transformations.
 207                 // To get around this we use arraycopy to copy the points
 208                 // to their final destination with correct overwrite
 209                 // handling and then transform them in place in the new
 210                 // safer location.
 211                 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 212                 // srcPts = dstPts;     // They are known to be equal.
 213                 srcOff = dstOff;
 214             }
 215             if (dstOff == srcOff && tx == 0.0 && ty == 0.0) {
 216                 return;
 217             }
 218         }
 219         for (int i = 0; i < numPts; i++) {
 220             dstPts[dstOff++] = srcPts[srcOff++] + tx;
 221             dstPts[dstOff++] = srcPts[srcOff++] + ty;
 222         }
 223     }
 224 
 225     @Override
 226     public void transform(float[] srcPts, int srcOff,
 227                           double[] dstPts, int dstOff,
 228                           int numPts)
 229     {
 230         double tx = this.mxt;
 231         double ty = this.myt;
 232         for (int i = 0; i < numPts; i++) {
 233             dstPts[dstOff++] = srcPts[srcOff++] + tx;
 234             dstPts[dstOff++] = srcPts[srcOff++] + ty;
 235         }
 236     }
 237 
 238     @Override
 239     public void transform(double[] srcPts, int srcOff,
 240                           float[] dstPts, int dstOff,
 241                           int numPts)
 242     {
 243         double tx = this.mxt;
 244         double ty = this.myt;
 245         for (int i = 0; i < numPts; i++) {
 246             dstPts[dstOff++] = (float) (srcPts[srcOff++] + tx);
 247             dstPts[dstOff++] = (float) (srcPts[srcOff++] + ty);
 248         }
 249     }
 250 
 251     @Override
 252     public void deltaTransform(float[] srcPts, int srcOff,
 253                                float[] dstPts, int dstOff,
 254                                int numPts)
 255     {
 256         if (srcPts != dstPts || srcOff != dstOff) {
 257             System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 258         }
 259     }
 260 
 261     @Override
 262     public void deltaTransform(double[] srcPts, int srcOff,
 263                                double[] dstPts, int dstOff,
 264                                int numPts)
 265     {
 266         if (srcPts != dstPts || srcOff != dstOff) {
 267             System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 268         }
 269     }
 270 
 271     @Override
 272     public void inverseTransform(float[] srcPts, int srcOff,
 273                                  float[] dstPts, int dstOff,
 274                                  int numPts)
 275     {
 276         float tx = (float) this.mxt;
 277         float ty = (float) this.myt;
 278         if (dstPts == srcPts) {
 279             if (dstOff > srcOff && dstOff < srcOff + numPts * 2) {
 280                 // If the arrays overlap partially with the destination higher
 281                 // than the source and we transform the coordinates normally
 282                 // we would overwrite some of the later source coordinates
 283                 // with results of previous transformations.
 284                 // To get around this we use arraycopy to copy the points
 285                 // to their final destination with correct overwrite
 286                 // handling and then transform them in place in the new
 287                 // safer location.
 288                 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 289                 // srcPts = dstPts;     // They are known to be equal.
 290                 srcOff = dstOff;
 291             }
 292             if (dstOff == srcOff && tx == 0.0f && ty == 0.0f) {
 293                 return;
 294             }
 295         }
 296         for (int i = 0; i < numPts; i++) {
 297             dstPts[dstOff++] = srcPts[srcOff++] - tx;
 298             dstPts[dstOff++] = srcPts[srcOff++] - ty;
 299         }
 300     }
 301 
 302     @Override
 303     public void inverseDeltaTransform(float[] srcPts, int srcOff,
 304                                       float[] dstPts, int dstOff,
 305                                       int numPts)
 306     {
 307         if (srcPts != dstPts || srcOff != dstOff) {
 308             System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 309         }
 310     }
 311 
 312     @Override
 313     public void inverseTransform(double[] srcPts, int srcOff,
 314                                  double[] dstPts, int dstOff,
 315                                  int numPts)
 316     {
 317         double tx = this.mxt;
 318         double ty = this.myt;
 319         if (dstPts == srcPts) {
 320             if (dstOff > srcOff && dstOff < srcOff + numPts * 2) {
 321                 // If the arrays overlap partially with the destination higher
 322                 // than the source and we transform the coordinates normally
 323                 // we would overwrite some of the later source coordinates
 324                 // with results of previous transformations.
 325                 // To get around this we use arraycopy to copy the points
 326                 // to their final destination with correct overwrite
 327                 // handling and then transform them in place in the new
 328                 // safer location.
 329                 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
 330                 // srcPts = dstPts;     // They are known to be equal.
 331                 srcOff = dstOff;
 332             }
 333             if (dstOff == srcOff && tx == 0f && ty == 0f) {
 334                 return;
 335             }
 336         }
 337         for (int i = 0; i < numPts; i++) {
 338             dstPts[dstOff++] = srcPts[srcOff++] - tx;
 339             dstPts[dstOff++] = srcPts[srcOff++] - ty;
 340         }
 341     }
 342 
 343     @Override
 344     public BaseBounds transform(BaseBounds bounds, BaseBounds result) {
 345         float minX = (float) (bounds.getMinX() + mxt);
 346         float minY = (float) (bounds.getMinY() + myt);
 347         float minZ = bounds.getMinZ();
 348         float maxX = (float) (bounds.getMaxX() + mxt);
 349         float maxY = (float) (bounds.getMaxY() + myt);
 350         float maxZ = bounds.getMaxZ();
 351         return result.deriveWithNewBounds(minX, minY, minZ, maxX, maxY, maxZ);
 352     }
 353 
 354     @Override
 355     public void transform(Rectangle rect, Rectangle result) {
 356         transform(rect, result, mxt, myt);
 357     }
 358 
 359     @Override
 360     public BaseBounds inverseTransform(BaseBounds bounds, BaseBounds result) {
 361         float minX = (float) (bounds.getMinX() - mxt);
 362         float minY = (float) (bounds.getMinY() - myt);
 363         float minZ = bounds.getMinZ();
 364         float maxX = (float) (bounds.getMaxX() - mxt);
 365         float maxY = (float) (bounds.getMaxY() - myt);
 366         float maxZ = bounds.getMaxZ();
 367         return result.deriveWithNewBounds(minX, minY, minZ, maxX, maxY, maxZ);
 368     }
 369 
 370     @Override
 371     public void inverseTransform(Rectangle rect, Rectangle result) {
 372         transform(rect, result, -mxt, -myt);
 373     }
 374 
 375     static void transform(Rectangle rect, Rectangle result,
 376                           double mxt, double myt)
 377     {
 378         int imxt = (int) mxt;
 379         int imyt = (int) myt;
 380         if (imxt == mxt && imyt == myt) {
 381             result.setBounds(rect);
 382             result.translate(imxt, imyt);
 383         } else {
 384             double x1 = rect.x + mxt;
 385             double y1 = rect.y + myt;
 386             double x2 = Math.ceil(x1 + rect.width);
 387             double y2 = Math.ceil(y1 + rect.height);
 388             x1 = Math.floor(x1);
 389             y1 = Math.floor(y1);
 390             result.setBounds((int) x1, (int) y1, (int) (x2 - x1), (int) (y2 - y1));
 391         }
 392     }
 393 
 394     @Override
 395     public void setToIdentity() {
 396         this.mxt = this.myt = 0.0;
 397     }
 398 
 399     @Override
 400     public void setTransform(BaseTransform xform) {
 401         if (!xform.isTranslateOrIdentity()) {
 402             degreeError(Degree.TRANSLATE_2D);
 403         }
 404         this.mxt = xform.getMxt();
 405         this.myt = xform.getMyt();
 406     }
 407 
 408     @Override
 409     public void invert() {
 410         this.mxt = -this.mxt;
 411         this.myt = -this.myt;
 412     }
 413 
 414     @Override
 415     public void restoreTransform(double mxx, double myx,
 416                                  double mxy, double myy,
 417                                  double mxt, double myt)
 418     {
 419         if (mxx != 1.0 || myx != 0.0 ||
 420             mxy != 0.0 || myy != 1.0)
 421         {
 422             degreeError(Degree.TRANSLATE_2D);
 423         }
 424         this.mxt = mxt;
 425         this.myt = myt;
 426     }
 427 
 428     @Override
 429     public void restoreTransform(double mxx, double mxy, double mxz, double mxt,
 430                                  double myx, double myy, double myz, double myt,
 431                                  double mzx, double mzy, double mzz, double mzt)
 432     {
 433         if (mxx != 1.0 || mxy != 0.0 || mxz != 0.0 ||
 434             myx != 0.0 || myy != 1.0 || myz != 0.0 ||
 435             mzx != 0.0 || mzy != 0.0 || mzz != 1.0 || mzt != 0.0)
 436         {
 437             degreeError(Degree.TRANSLATE_2D);
 438         }
 439         this.mxt = mxt;
 440         this.myt = myt;
 441     }
 442 
 443     @Override
 444     public BaseTransform deriveWithTranslation(double mxt, double myt) {
 445         this.mxt += mxt;
 446         this.myt += myt;
 447         return this;
 448     }
 449 
 450     @Override
 451     public BaseTransform deriveWithTranslation(double mxt, double myt, double mzt) {
 452         if (mzt == 0.0) {
 453             this.mxt += mxt;
 454             this.myt += myt;
 455             return this;
 456         }
 457         Affine3D a = new Affine3D();
 458         a.translate(this.mxt + mxt, this.myt + myt, mzt);
 459         return a;
 460     }
 461 
 462     @Override
 463     public BaseTransform deriveWithScale(double mxx, double myy, double mzz) {
 464         if (mzz == 1.0) {
 465             if (mxx == 1.0 && myy == 1.0) {
 466                 return this;
 467             }
 468             Affine2D a = new Affine2D();
 469             a.translate(this.mxt, this.myt);
 470             a.scale(mxx, myy);
 471             return a;
 472         }
 473         Affine3D a = new Affine3D();
 474         a.translate(this.mxt, this.myt);
 475         a.scale(mxx, myy, mzz);
 476         return a;
 477 
 478     }
 479 
 480     @Override
 481     public BaseTransform deriveWithRotation(double theta,
 482             double axisX, double axisY, double axisZ) {
 483         if (theta == 0.0) {
 484             return this;
 485         }
 486         if (almostZero(axisX) && almostZero(axisY)) {
 487             if (axisZ == 0.0) {
 488                 return this;
 489             }
 490             Affine2D a = new Affine2D();
 491             a.translate(this.mxt, this.myt);
 492             if (axisZ > 0) {
 493                 a.rotate(theta);
 494             } else if (axisZ < 0) {
 495                 a.rotate(-theta);
 496             }
 497             return a;
 498         }
 499         Affine3D a = new Affine3D();
 500         a.translate(this.mxt, this.myt);
 501         a.rotate(theta, axisX, axisY, axisZ);
 502         return a;
 503     }
 504 
 505     @Override
 506     public BaseTransform deriveWithPreTranslation(double mxt, double myt) {
 507         this.mxt += mxt;
 508         this.myt += myt;
 509         return this;
 510     }
 511 
 512     @Override
 513     public BaseTransform deriveWithConcatenation(double mxx, double myx,
 514                                                  double mxy, double myy,
 515                                                  double mxt, double myt)
 516     {
 517         if (mxx == 1.0 && myx == 0.0 && mxy == 0.0 && myy == 1.0) {
 518             this.mxt += mxt;
 519             this.myt += myt;
 520             return this;
 521         } else {
 522             return new Affine2D(mxx, myx,
 523                                 mxy, myy,
 524                                 this.mxt + mxt, this.myt + myt);
 525         }
 526     }
 527 
 528     @Override
 529     public BaseTransform deriveWithConcatenation(
 530             double mxx,   double mxy,   double mxz,   double mxt,
 531             double myx,   double myy,   double myz,   double myt,
 532             double mzx,   double mzy,   double mzz,   double mzt) {
 533         if (                                   mxz == 0.0
 534                                             && myz == 0.0
 535                 && mzx == 0.0 && mzy == 0.0 && mzz == 1.0 && mzt == 0.0) {
 536             return deriveWithConcatenation(mxx, myx,
 537                                            mxy, myy,
 538                                            mxt, myt);
 539         }
 540 
 541         return new Affine3D(mxx, mxy, mxz, mxt + this.mxt,
 542                             myx, myy, myz, myt + this.myt,
 543                             mzx, mzy, mzz, mzt);
 544     }
 545 
 546     @Override
 547     public BaseTransform deriveWithConcatenation(BaseTransform tx) {
 548         if (tx.isTranslateOrIdentity()) {
 549             this.mxt += tx.getMxt();
 550             this.myt += tx.getMyt();
 551             return this;
 552         } else if (tx.is2D()) {
 553             return getInstance(tx.getMxx(), tx.getMyx(),
 554                                tx.getMxy(), tx.getMyy(),
 555                                this.mxt + tx.getMxt(), this.myt + tx.getMyt());
 556         } else {
 557             Affine3D t3d = new Affine3D(tx);
 558             t3d.preTranslate(this.mxt, this.myt, 0.0);
 559             return t3d;
 560         }
 561     }
 562 
 563     @Override
 564     public BaseTransform deriveWithPreConcatenation(BaseTransform tx) {
 565         if (tx.isTranslateOrIdentity()) {
 566             this.mxt += tx.getMxt();
 567             this.myt += tx.getMyt();
 568             return this;
 569         } else if (tx.is2D()) {
 570             Affine2D t2d = new Affine2D(tx);
 571             t2d.translate(this.mxt, this.myt);
 572             return t2d;
 573         } else {
 574             Affine3D t3d = new Affine3D(tx);
 575             t3d.translate(this.mxt, this.myt, 0.0);
 576             return t3d;
 577         }
 578     }
 579 
 580     @Override
 581     public BaseTransform deriveWithNewTransform(BaseTransform tx) {
 582         if (tx.isTranslateOrIdentity()) {
 583             this.mxt = tx.getMxt();
 584             this.myt = tx.getMyt();
 585             return this;
 586         } else {
 587             return getInstance(tx);
 588         }
 589     }
 590 
 591     @Override
 592     public BaseTransform createInverse() {
 593         if (isIdentity()) {
 594             return IDENTITY_TRANSFORM;
 595         } else {
 596             return new Translate2D(-this.mxt, -this.myt);
 597         }
 598     }
 599 
 600     // Round values to sane precision for printing
 601     // Note that Math.sin(Math.PI) has an error of about 10^-16
 602     private static double _matround(double matval) {
 603         return Math.rint(matval * 1E15) / 1E15;
 604     }
 605 
 606     @Override
 607     public String toString() {
 608         return ("Translate2D["
 609                 + _matround(mxt) + ", "
 610                 + _matround(myt) + "]");
 611     }
 612 
 613     @Override
 614     public BaseTransform copy() {
 615         return new Translate2D(this.mxt, this.myt);
 616     }
 617 
 618     @Override
 619     public boolean equals(Object obj) {
 620         if (obj instanceof BaseTransform) {
 621             BaseTransform tx = (BaseTransform) obj;
 622             return (tx.isTranslateOrIdentity() &&
 623                     tx.getMxt() == this.mxt &&
 624                     tx.getMyt() == this.myt);
 625         }
 626         return false;
 627     }
 628 
 629     private static final long BASE_HASH;
 630     static {
 631         long bits = 0;
 632         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzz());
 633         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzy());
 634         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzx());
 635         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMyz());
 636         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMxz());
 637         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMyy());
 638         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMyx());
 639         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMxy());
 640         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMxx());
 641         bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzt());
 642         BASE_HASH = bits;
 643     }
 644 
 645     @Override
 646     public int hashCode() {
 647         if (isIdentity()) return 0;
 648         long bits = BASE_HASH;
 649         bits = bits * 31 + Double.doubleToLongBits(getMyt());
 650         bits = bits * 31 + Double.doubleToLongBits(getMxt());
 651         return (((int) bits) ^ ((int) (bits >> 32)));
 652     }
 653 }