1 package cz.cuni.amis.pogamut.base3d.worldview.object; 2 3 import java.beans.PropertyEditorManager; 4 import java.beans.PropertyEditorSupport; 5 import java.io.Serializable; 6 import java.util.Locale; 7 import java.util.regex.Matcher; 8 import java.util.regex.Pattern; 9 10 import javax.vecmath.Tuple3d; 11 import javax.vecmath.Vector3d; 12 13 /** 14 * Velocity within the world. 15 * 16 * Direction of the velocity is represented as a vector within the world's 17 * coordinates. Size of the velocity is represented by length of that vector. 18 * 19 * @author Juraj 'Loque' Simlovic 20 */ 21 public class Velocity implements ILocomotive, Serializable, Cloneable 22 { 23 /** 24 * Velocity representing NONE. 25 */ 26 public static final Velocity NONE = new Velocity(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); 27 28 static { 29 // register property editor, otherwise this class won't be introspectable 30 PropertyEditorManager.registerEditor(Velocity.class, Velocity.PropertyEditor.class); 31 } 32 33 /** 34 * Property editor for Velocity. Accepts same format as Location. 35 */ 36 public static class PropertyEditor extends PropertyEditorSupport { 37 38 @Override 39 public String getAsText() { 40 if (getValue() != null) { 41 return getValue().toString(); 42 } else { 43 return "null"; 44 } 45 } 46 47 @Override 48 public void setAsText(String s) { 49 if ("null".equals(s.trim())) { 50 setValue(null); 51 } else { 52 double[] d = Location.PropertyEditor.parseNumberArray(s); 53 if (d.length != 3) { 54 throw new IllegalArgumentException(); 55 } 56 setValue(new Velocity(d)); 57 } 58 } 59 } 60 61 @Override 62 public Velocity clone() { 63 return new Velocity(this); 64 } 65 66 public Vector3d asVector3d() { 67 return new Vector3d(x, y, z); 68 } 69 70 public Location asLocation() { 71 return new Location(x,y,z); 72 } 73 74 /** X coordinate. */ 75 public double x = 0; 76 /** Y coordinate. */ 77 public double y = 0; 78 /** Z coordinate. */ 79 public double z = 0; 80 81 /* ********************************************************************** */ 82 83 /** 84 * X coordinate. 85 * @return X coordinate. 86 */ 87 public double getX() 88 { 89 return x; 90 } 91 92 /** 93 * Y coordinate. 94 * @return Y coordinate. 95 */ 96 public double getY() 97 { 98 return y; 99 } 100 101 /** 102 * Z coordinate. 103 * @return Z coordinate. 104 */ 105 public double getZ() 106 { 107 return z; 108 } 109 110 /* ********************************************************************** */ 111 112 /** 113 * Sets the X coordinate. 114 * @return self. 115 */ 116 public Velocity setX(double x) 117 { 118 this.x = x; 119 return this; 120 } 121 122 /** 123 * Sets the Y coordinate. 124 * @return self. 125 */ 126 public Velocity setY(double y) 127 { 128 this.y = y; 129 return this; 130 } 131 132 /** 133 * Sets the Z coordinate. 134 * @return self. 135 */ 136 public Velocity setZ(double z) 137 { 138 this.z = z; 139 return this; 140 } 141 142 /* ********************************************************************* */ 143 144 /** 145 * Tells, whether the velocity is zero. 146 * @return True if the velocity is 0 in all directions; false otherwise. 147 */ 148 public boolean isZero () 149 { 150 // test all three elements 151 return (x == 0) && (y == 0) && (z == 0); 152 } 153 154 /** 155 * Tells, whether the velocity is zero (with tolerance of 'epsilon'). 156 * @return True if the velocity is 0 in all directions; false otherwise. 157 */ 158 public boolean isZero (double epsilon) 159 { 160 // test all three elements 161 return (Math.abs(x) < epsilon) && (Math.abs(y) < epsilon) && (Math.abs(z) < epsilon); 162 } 163 164 /** 165 * Tells, whether the velocity is zero in planar coordinates. 166 * @return True if the velocity is 0 in planar directions; false otherwise. 167 */ 168 public boolean isPlanarZero () 169 { 170 // test two planar elements 171 return (x == 0) && (y == 0); 172 } 173 174 /** 175 * Retreives size of the velocity. 176 * @return Size of the velocity. 177 */ 178 public double size () 179 { 180 // calculate sqare of the size, then sqrt 181 return Math.sqrt(x * x + y * y + z * z); 182 } 183 184 /** 185 * Retreives squared size of the velocity. 186 * @return Size of the velocity to the power of 2. 187 */ 188 public double sizeSquare () 189 { 190 // calculate sqare of the size 191 return x * x + y * y + z * z; 192 } 193 194 /* ********************************************************************* */ 195 196 /** 197 * Retreives normalized vector of the velocity. 198 * @return Velocity normalized to the size of 1. 199 */ 200 public Velocity normalize () 201 { 202 // calculate reciprocal value of the size of the vector 203 double d = 1 / Math.sqrt(x * x + y * y + z * z); 204 // diminish all three directions by the size of the vector 205 return new Velocity (x * d, y * d, z * d); 206 } 207 208 /** 209 * Negates values of all three coordinates. 210 * @return Velocity with all three coordinates negated. 211 */ 212 public Velocity negate () 213 { 214 // create velocity of negative values 215 return new Velocity (-x, -y, -z); 216 } 217 218 /** 219 * Converts values of all three coordinates to absolute values. 220 * @return Velocity with all three coordinates <i>absoluted</i>. 221 */ 222 public Velocity absolute () 223 { 224 // create velocity of absoluted values 225 return new Velocity (Math.abs(x), Math.abs(y), Math.abs(z)); 226 } 227 228 /** 229 * Scales values of all three coordinates by given multiplier. 230 * @param d Scaling multiplier. 231 * @return Velocity with all three coordinates negated. 232 */ 233 public Velocity scale (double d) 234 { 235 // create velocity with scaled values 236 return new Velocity (x * d, y * d, z * d); 237 } 238 239 /* ********************************************************************* */ 240 241 /** 242 * Computes dot product of this and other given velocity. 243 * @param v Second velocity to be computed upon. 244 * @return Dot product (scalar product) of the two velocities. 245 */ 246 public double dot (Velocity v) 247 { 248 // calculate dot product of the vectors 249 return x * v.x + y * v.y + z * v.z; 250 } 251 252 /** 253 * Computes dot product of two given velocities. 254 * @param v1 First velocity to be computed upon. 255 * @param v2 Second velocity to be computed upon. 256 * @return Dot product (scalar product) of the two velocities. 257 */ 258 public static double dot (Velocity v1, Velocity v2) 259 { 260 // calculate dot product of the vectors 261 return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; 262 } 263 264 /* ********************************************************************* */ 265 266 /** 267 * Computes cross product of this and other given velocity. 268 * @param v Second velocity to be computed upon. 269 * @return Cross product of the two velocities. 270 */ 271 public Velocity cross (Velocity v) 272 { 273 // calculate cross product of the vectors 274 return new Velocity ( 275 y * v.z - z * v.y, 276 z * v.x - x * v.z, 277 x * v.y - y * v.x 278 ); 279 } 280 281 /** 282 * Computes cross product of two given velocities. 283 * @param v1 First velocity to be computed upon. 284 * @param v2 Second velocity to be computed upon. 285 * @return Cross product of the two velocities. 286 */ 287 public static Velocity cross (Velocity v1, Velocity v2) 288 { 289 // calculate cross product of the vectors 290 return new Velocity ( 291 v1.y * v2.z - v1.z * v2.y, 292 v1.z * v2.x - v1.x * v2.z, 293 v1.x * v2.y - v1.y * v2.x 294 ); 295 } 296 297 /* ********************************************************************* */ 298 299 /** 300 * Retreives sum of this velocity and given velocity. 301 * @param v Velocity to by added to this velocity. 302 * @return Sum of the two velocities. 303 */ 304 public Velocity add (Velocity v) 305 { 306 // create sum of the velocities 307 return new Velocity (x + v.x, y + v.y, z + v.z); 308 } 309 310 /** 311 * Retreives sum of two given velocities. 312 * @param v1 First velocity to by summed. 313 * @param v2 Second velocity to by summed. 314 * @return Sum of the two velocities. 315 */ 316 public static Velocity add (Velocity v1, Velocity v2) 317 { 318 // create sum of the velocities 319 return new Velocity (v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); 320 } 321 322 /** 323 * Retreives subtraction of given velocity from this velocity. 324 * @param v Velocity to be subtracted. 325 * @return Subtraction of the two velocities. 326 */ 327 public Velocity sub (Velocity v) 328 { 329 // create substraction of the velocities 330 return new Velocity (x - v.x, y - v.y, z - v.z); 331 } 332 333 /** 334 * Retreives subtraction of two given velocities. 335 * @param v1 Velocity to be subtracted from. 336 * @param v2 Velocity to be subtracted. 337 * @return Subtraction of the two velocities. 338 */ 339 public static Velocity sub (Velocity v1, Velocity v2) 340 { 341 // create substraction of the velocities 342 return new Velocity (v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); 343 } 344 345 /* ********************************************************************* */ 346 347 /** 348 * Linearly interpolates between this velocity and given velocity. 349 * @param v Velocity to be interpolated to. 350 * @param d Interpolation parameter. 351 * @return Linear interpolation between the two velocities. 352 */ 353 public Velocity interpolate(Velocity v, double d) 354 { 355 // from the other side 356 double d1 = 1.0D - d; 357 // create interpolation of the velocities 358 return new Velocity (d1 * x + d * v.x, d1 * y + d * v.y, d1 * z + d * v.z); 359 } 360 361 /** 362 * Linearly interpolates between two given velocities. 363 * @param v1 Velocity to be interpolated from. 364 * @param v2 Velocity to be interpolated to. 365 * @param d Interpolation parameter. 366 * @return Linear interpolation between the two velocities. 367 */ 368 public static Velocity interpolate(Velocity v1, Velocity v2, double d) 369 { 370 // from the other side 371 double d1 = 1.0D - d; 372 // create interpolation of the velocities 373 return new Velocity (d1 * v1.x + d * v2.x, d1 * v1.y + d * v2.y, d1 * v1.z + d * v2.z); 374 } 375 376 /* ********************************************************************* */ 377 378 /** 379 * Tells, whether this velocity equals to given velocity. 380 * @param v Velocity to be compared with. 381 * @return True, if the velocities have the same values of all three 382 * corresponding coordinates. 383 */ 384 public boolean equals (Velocity v) 385 { 386 // test all three elements 387 return (x == v.x) && (y == v.y) && (z == v.z); 388 } 389 390 /** 391 * Tells, whether two given velocities equal. 392 * @param v1 First velocity to comapre. 393 * @param v2 Second velocity to comapre. 394 * @return True, if the velocities have the same values of all three 395 * corresponding coordinates. 396 */ 397 public static boolean equal (Velocity v1, Velocity v2) 398 { 399 // test all three elements 400 return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); 401 } 402 403 /** 404 * Tells, whether the distance between coordinates of this velocity 405 * and given velocity is less than or equal to the given epsilon. 406 * @param v Velocity to comapre with. 407 * @param epsilon Epsilon to compare with. 408 * @return True, if the distance between the velocities is less than the 409 * epsilon, false otherwise. 410 */ 411 public boolean equals (Velocity v, double epsilon) 412 { 413 double d; 414 415 // x axes distance 416 d = x - v.x; 417 if((d >= 0 ? d : -d) > epsilon) 418 return false; 419 420 // y axes distance 421 d = y - v.y; 422 if((d >= 0.0D ? d : -d) > epsilon) 423 return false; 424 425 // z axes distance 426 d = z - v.z; 427 if((d >= 0.0D ? d : -d) > epsilon) 428 return false; 429 430 // aye, aye, sir.. 431 return true; 432 } 433 434 /** 435 * Tells, whether the distance between coordinates of two given velocities 436 * is less than or equal to the given epsilon. 437 * @param v1 First velocity to comapre. 438 * @param v2 Second velocity to comapre. 439 * @param epsilon Epsilon to compare with. 440 * @return True, if the distance between the velocities is less than the 441 * epsilon, false otherwise. 442 */ 443 public static boolean equal (Velocity v1, Velocity v2, double epsilon) 444 { 445 double d; 446 447 // x axes distance 448 d = v1.x - v2.x; 449 if((d >= 0 ? d : -d) > epsilon) 450 return false; 451 452 // y axes distance 453 d = v1.y - v2.y; 454 if((d >= 0.0D ? d : -d) > epsilon) 455 return false; 456 457 // z axes distance 458 d = v1.z - v2.z; 459 if((d >= 0.0D ? d : -d) > epsilon) 460 return false; 461 462 // aye, aye, sir.. 463 return true; 464 } 465 466 /* ********************************************************************* */ 467 468 /** 469 * Projects the velocity into the (x, y) plane, i.e. removes z coordinate. 470 * @return Aligned velocity, with z coordinate set to zero. 471 */ 472 public Velocity align () 473 { 474 // create aligned velocity 475 return new Velocity (x, y, 0); 476 } 477 478 /** 479 * Computes sideways velocity, i.e. orthogonal velocity to projection of 480 * this velocity to (x, y) plane. 481 * 482 * <p>Note: Ignores the z coordinate whatsoever, which is the same as 483 * projecting the velocity to the (x, y) plane. Calculates orthogonal 484 * vector to this projection. Returns vector (y, -x, 0). 485 * 486 * @return Aligned velocity, with z coordinate set to zero. 487 */ 488 public Velocity sideways () 489 { 490 // create aligned velocity 491 return new Velocity (y, -x, 0); 492 } 493 494 /* ********************************************************************* */ 495 496 /** 497 * Retreives the velocity itself to implement {@link ILocomotive}. 498 * @return The velocity itself (note: does not create a copy). 499 */ 500 @Override 501 public Velocity getVelocity() 502 { 503 return this; 504 } 505 506 /** 507 * Retreives javax.vecmath.Vector3d representation of the velocity. 508 * @return javax.vecmath.Vector3d representation with x, y and z values set. 509 */ 510 public Vector3d getVector3d() 511 { 512 return new Vector3d (x, y, z); 513 } 514 515 /* ********************************************************************** */ 516 517 /** 518 * Creates velocity with all values set to zeroes. 519 */ 520 public Velocity() 521 { 522 } 523 524 /** 525 * Creates velocity same as the the passed one. 526 * @param velocity original velocity that will be copied 527 */ 528 public Velocity(Velocity velocity) { 529 this.x = velocity.getX(); 530 this.y = velocity.getY(); 531 this.z = velocity.getZ(); 532 } 533 534 /** 535 * Creates velocity with specified coordinates. 536 * @param x X coordinate. 537 * @param y Y coordinate. 538 * @param z Z coordinate. 539 */ 540 public Velocity(double x, double y, double z) 541 { 542 this.x = x; 543 this.y = y; 544 this.z = z; 545 } 546 547 /** 548 * Creates velocity with specified planar coordinates. Sets z to zero. 549 * @param x X coordinate. 550 * @param y Y coordinate. 551 */ 552 public Velocity(double x, double y) 553 { 554 this.x = x; 555 this.y = y; 556 } 557 558 /** 559 * Creates velocity from array of three doubles. 560 * Sets x = d[0], y = d[1] and z = d[2]. 561 * @param d Array of (at least) three doubles to be used for creation. 562 */ 563 public Velocity(double d[]) 564 { 565 this.x = d[0]; 566 this.y = d[1]; 567 this.z = d[2]; 568 } 569 570 /** 571 * Creates velocity from specified 3D vector. 572 * @param v Vector in space to be used for creation. 573 */ 574 public Velocity(Tuple3d v) 575 { 576 this.x = v.x; 577 this.y = v.y; 578 this.z = v.z; 579 } 580 581 /** 582 * Pattern used to parse {@link Velocity#toString()} in {@link Velocity#Velocity(String)}. 583 */ 584 public static final Pattern velocityPattern = Pattern.compile("\\[([-+]?[0-9]+(\\.[0-9]+){0,1})\\; ([-+]?[0-9]+(\\.[0-9]+){0,1})\\; ([-+]?[0-9]+(\\.[0-9]+){0,1})\\]"); 585 586 /** 587 * Parses the velocity from the "string" generated by {@link Velocity#toString()}. If it fails, 588 * it throws RuntimeException. 589 * 590 * @param string 591 */ 592 public Velocity(String string) { 593 Matcher m = velocityPattern.matcher(string); 594 if (m.find()) { 595 String strX = m.group(1); 596 String strY = m.group(3); 597 String strZ = m.group(5); 598 try { 599 this.x = Double.parseDouble(strX); 600 } catch (Exception e) { 601 throw new RuntimeException("String '" + string + "', was not matched as Velocity, because X-coordinate '" + strX + "' is not a number."); 602 } 603 try { 604 this.y = Double.parseDouble(strY); 605 } catch (Exception e) { 606 throw new RuntimeException("String '" + string + "', was not matched as Velocity, because Y-coordinate '" + strY + "' is not a number."); 607 } 608 try { 609 this.z = Double.parseDouble(strZ); 610 } catch (Exception e) { 611 throw new RuntimeException("String '" + string + "', was not matched as Velocity, because Z-coordinate '" + strZ + "' is not a number."); 612 } 613 } else { 614 throw new RuntimeException("String '" + string +"' was not matched as Velocity."); 615 } 616 } 617 618 /* ********************************************************************** */ 619 620 @Override 621 public String toString() 622 { 623 return String.format (Locale.ENGLISH, "[%.2f; %.2f; %.2f]", x, y, z); 624 } 625 626 }