View Javadoc

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 }