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 }