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 }