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