dbd565a0f6b943709ec369e446634083313fd213
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / widgets / Utils.java
1 /*****************************************************************************
2 * Copyright (c) 2007, 2014 Intel Corporation, Ericsson
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Intel Corporation - Initial API and implementation
10 * Ruslan A. Scherbakov, Intel - Initial API and implementation
11 * Alvaro Sanchez-Leon - Udpated for TMF
12 * Patrick Tasse - Refactoring
13 * Marc-Andre Laperle - Add time zone preference
14 *****************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
17
18 import java.text.NumberFormat;
19 import java.text.SimpleDateFormat;
20 import java.util.Date;
21 import java.util.Iterator;
22 import java.util.TimeZone;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.swt.graphics.Color;
27 import org.eclipse.swt.graphics.Device;
28 import org.eclipse.swt.graphics.GC;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.widgets.Display;
32 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
33 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimePreferences;
34 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
35 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
36
37 /**
38 * General utilities and definitions used by the time graph widget
39 *
40 * @author Alvaro Sanchez-Leon
41 * @author Patrick Tasse
42 */
43 public class Utils {
44
45 private Utils() {
46 }
47
48 /** Time format for dates and timestamp */
49 public enum TimeFormat {
50 /** Relative to the start of the trace */
51 RELATIVE,
52
53 /**
54 * Absolute timestamp (ie, relative to the Unix epoch)
55 */
56 CALENDAR,
57
58 /**
59 * Timestamp displayed as a simple number
60 */
61 NUMBER,
62
63 /**
64 * Timestamp displayed as cycles
65 */
66 CYCLES
67 }
68
69 /**
70 * Timestamp resolution
71 */
72 public static enum Resolution {
73 /** seconds */
74 SECONDS,
75
76 /** milliseconds */
77 MILLISEC,
78
79 /** microseconds */
80 MICROSEC,
81
82 /** nanoseconds */
83 NANOSEC
84 }
85
86 private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$
87 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
88 private static final long HOURS_PER_DAY = 24;
89 private static final long MIN_PER_HOUR = 60;
90 private static final long SEC_PER_MIN = 60;
91 private static final long SEC_IN_NS = 1000000000;
92 private static final long MILLISEC_IN_NS = 1000000;
93
94 /**
95 * Update the time and date formats to use the current time zone
96 */
97 public static void updateTimeZone() {
98 TimeZone timeZone = TmfTimePreferences.getTimeZone();
99 TIME_FORMAT.setTimeZone(timeZone);
100 DATE_FORMAT.setTimeZone(timeZone);
101 }
102
103 static Rectangle clone(Rectangle source) {
104 return new Rectangle(source.x, source.y, source.width, source.height);
105 }
106
107 /**
108 * Initialize a Rectangle object to default values (all equal to 0)
109 *
110 * @param rect
111 * The Rectangle to initialize
112 */
113 public static void init(Rectangle rect) {
114 rect.x = 0;
115 rect.y = 0;
116 rect.width = 0;
117 rect.height = 0;
118 }
119
120 /**
121 * Initialize a Rectangle object with all the given values
122 *
123 * @param rect
124 * The Rectangle object to initialize
125 * @param x
126 * The X coordinate
127 * @param y
128 * The Y coordinate
129 * @param width
130 * The width of the rectangle
131 * @param height
132 * The height of the rectangle
133 */
134 public static void init(Rectangle rect, int x, int y, int width, int height) {
135 rect.x = x;
136 rect.y = y;
137 rect.width = width;
138 rect.height = height;
139 }
140
141 /**
142 * Initialize a Rectangle object to another existing Rectangle's values.
143 *
144 * @param rect
145 * The Rectangle to initialize
146 * @param source
147 * The reference Rectangle to copy
148 */
149 public static void init(Rectangle rect, Rectangle source) {
150 rect.x = source.x;
151 rect.y = source.y;
152 rect.width = source.width;
153 rect.height = source.height;
154 }
155
156 /**
157 * Reduce the size of a given rectangle by the given amounts.
158 *
159 * @param rect
160 * The rectangle to modify
161 * @param x
162 * The reduction in width
163 * @param y
164 * The reduction in height
165 */
166 public static void deflate(Rectangle rect, int x, int y) {
167 rect.x += x;
168 rect.y += y;
169 rect.width -= x + x;
170 rect.height -= y + y;
171 }
172
173 /**
174 * Increase the size of a given rectangle by the given amounts.
175 *
176 * @param rect
177 * The rectangle to modify
178 * @param x
179 * The augmentation in width
180 * @param y
181 * The augmentation in height
182 */
183 public static void inflate(Rectangle rect, int x, int y) {
184 rect.x -= x;
185 rect.y -= y;
186 rect.width += x + x;
187 rect.height += y + y;
188 }
189
190 static void dispose(Color col) {
191 if (null != col) {
192 col.dispose();
193 }
194 }
195
196 /**
197 * Get the resulting color from a mix of two existing ones for a given
198 * display.
199 *
200 * @param display
201 * The display device (which might affect the color conversion)
202 * @param c1
203 * The first color
204 * @param c2
205 * The second color
206 * @param w1
207 * The gamma level for color 1
208 * @param w2
209 * The gamma level for color 2
210 * @return The resulting color
211 */
212 public static Color mixColors(Device display, Color c1, Color c2, int w1,
213 int w2) {
214 return new Color(display, (w1 * c1.getRed() + w2 * c2.getRed())
215 / (w1 + w2), (w1 * c1.getGreen() + w2 * c2.getGreen())
216 / (w1 + w2), (w1 * c1.getBlue() + w2 * c2.getBlue())
217 / (w1 + w2));
218 }
219
220 /**
221 * Get the system color with the given ID.
222 *
223 * @param id
224 * The color ID
225 * @return The resulting color
226 */
227 public static Color getSysColor(int id) {
228 Color col = Display.getCurrent().getSystemColor(id);
229 return new Color(col.getDevice(), col.getRGB());
230 }
231
232 /**
233 * Get the resulting color from a mix of two existing ones for the current
234 * display.
235 *
236 * @param col1
237 * The first color
238 * @param col2
239 * The second color
240 * @param w1
241 * The gamma level for color 1
242 * @param w2
243 * The gamma level for color 2
244 * @return The resulting color
245 */
246 public static Color mixColors(Color col1, Color col2, int w1, int w2) {
247 return mixColors(Display.getCurrent(), col1, col2, w1, w2);
248 }
249
250 /**
251 * Draw text in a rectangle.
252 *
253 * @param gc
254 * The SWT GC object
255 * @param text
256 * The text to draw
257 * @param rect
258 * The rectangle object which is being drawn
259 * @param transp
260 * If true the background will be transparent
261 * @return The width of the written text
262 */
263 public static int drawText(GC gc, String text, Rectangle rect, boolean transp) {
264 Point size = gc.stringExtent(text);
265 gc.drawText(text, rect.x, rect.y, transp);
266 return size.x;
267 }
268
269 /**
270 * Draw text at a given location.
271 *
272 * @param gc
273 * The SWT GC object
274 * @param text
275 * The text to draw
276 * @param x
277 * The X coordinate of the starting point
278 * @param y
279 * the Y coordinate of the starting point
280 * @param transp
281 * If true the background will be transparent
282 * @return The width of the written text
283 */
284 public static int drawText(GC gc, String text, int x, int y, boolean transp) {
285 Point size = gc.stringExtent(text);
286 gc.drawText(text, x, y, transp);
287 return size.x;
288 }
289
290 /**
291 * Draw text in a rectangle, trimming the text to prevent exceeding the specified width.
292 *
293 * @param gc
294 * The SWT GC object
295 * @param text
296 * The string to be drawn
297 * @param x
298 * The x coordinate of the top left corner of the rectangular area where the text is to be drawn
299 * @param y
300 * The y coordinate of the top left corner of the rectangular area where the text is to be drawn
301 * @param width
302 * The width of the area to be drawn
303 * @param isCentered
304 * If <code>true</code> the text will be centered in the available width if space permits
305 * @param isTransparent
306 * If <code>true</code> the background will be transparent, otherwise it will be opaque
307 * @return The number of characters written
308 */
309 public static int drawText(GC gc, String text, int x, int y, int width, boolean isCentered, boolean isTransparent) {
310 if (width < 1) {
311 return 0;
312 }
313
314 int len = text.length();
315 int textWidth = 0;
316 boolean isReallyCentered = isCentered;
317 int realX = x;
318
319 while (len > 0) {
320 textWidth = gc.stringExtent(text.substring(0, len)).x;
321 if (textWidth <= width) {
322 break;
323 }
324 isReallyCentered = false;
325 len--;
326 }
327 if (len > 0) {
328 if (isReallyCentered) {
329 realX += (width - textWidth) / 2;
330 }
331 gc.drawText(text.substring(0, len), realX, y, isTransparent);
332 }
333 return len;
334 }
335
336 /**
337 * Draw text in a rectangle, trimming the text to prevent exceeding the specified width.
338 *
339 * @param gc
340 * The SWT GC object
341 * @param text
342 * The string to be drawn
343 * @param x
344 * The x coordinate of the top left corner of the rectangular area where the text is to be drawn
345 * @param y
346 * The y coordinate of the top left corner of the rectangular area where the text is to be drawn
347 * @param width
348 * The width of the area to be drawn
349 * @param height
350 * The height of the area to be drawn
351 * @param isCentered
352 * If <code>true</code> the text will be centered in the available area if space permits
353 * @param isTransparent
354 * If <code>true</code> the background will be transparent, otherwise it will be opaque
355 * @return The number of characters written
356 * @since 2.0
357 */
358 public static int drawText(GC gc, String text, int x, int y, int width, int height, boolean isCentered, boolean isTransparent) {
359 if (width < 1) {
360 return 0;
361 }
362
363 int len = text.length();
364 int textWidth = 0;
365 boolean isCenteredWidth = isCentered;
366 int realX = x;
367 int realY = y;
368
369 Point textExtent = null;
370 while (len > 0) {
371 textExtent = gc.textExtent(text.substring(0, len));
372 textWidth = textExtent.x;
373 if (textWidth <= width) {
374 break;
375 }
376 isCenteredWidth = false;
377 len--;
378 textExtent = gc.textExtent(text.substring(0, len));
379 }
380 if (len > 0) {
381 if (isCenteredWidth) {
382 realX += (width - textWidth) / 2;
383 }
384 if (isCentered && textExtent != null) {
385 realY += (height - textExtent.y) / 2 - 1;
386 }
387 gc.drawText(text.substring(0, len), realX, realY, isTransparent);
388 }
389 return len;
390 }
391
392 /**
393 * Formats time in format: MM:SS:NNN
394 *
395 * @param time time
396 * @param format 0: MMMM:ss:nnnnnnnnn, 1: HH:MM:ss MMM.mmmm.nnn
397 * @param resolution the resolution
398 * @return the formatted time
399 */
400 public static String formatTime(long time, TimeFormat format, Resolution resolution) {
401 switch (format) {
402 case CALENDAR:
403 return formatTimeAbs(time, resolution);
404 case NUMBER:
405 return NumberFormat.getInstance().format(time);
406 case CYCLES:
407 return NumberFormat.getInstance().format(time) + Messages.Utils_ClockCyclesUnit;
408 case RELATIVE:
409 default:
410 }
411
412 StringBuffer str = new StringBuffer();
413 long t = time;
414 boolean neg = t < 0;
415 if (neg) {
416 t = -t;
417 str.append('-');
418 }
419
420 long sec = t / SEC_IN_NS;
421 str.append(sec);
422 String ns = formatNs(t, resolution);
423 if (!ns.equals("")) { //$NON-NLS-1$
424 str.append('.');
425 str.append(ns);
426 }
427
428 return str.toString();
429 }
430
431 /**
432 * From input time in nanoseconds, convert to Date format YYYY-MM-dd
433 *
434 * @param absTime
435 * The source time, in ns
436 * @return the formatted date
437 */
438 public static String formatDate(long absTime) {
439 String sdate = DATE_FORMAT.format(new Date(absTime / MILLISEC_IN_NS));
440 return sdate;
441 }
442
443 /**
444 * Formats time in ns to Calendar format: HH:MM:SS MMM.mmm.nnn
445 *
446 * @param time
447 * The source time, in ns
448 * @param res
449 * The resolution to use
450 * @return the formatted time
451 */
452 public static String formatTimeAbs(long time, Resolution res) {
453 StringBuffer str = new StringBuffer();
454
455 // format time from nanoseconds to calendar time HH:MM:SS
456 String stime = TIME_FORMAT.format(new Date(time / MILLISEC_IN_NS));
457 str.append(stime);
458 str.append('.');
459 // append the Milliseconds, MicroSeconds and NanoSeconds as specified in
460 // the Resolution
461 str.append(formatNs(time, res));
462 return str.toString();
463 }
464
465 /**
466 * Formats time delta
467 *
468 * @param delta
469 * The time delta, in ns
470 * @param format
471 * The time format to use
472 * @param resolution
473 * The resolution to use
474 * @return the formatted time delta
475 */
476 public static String formatDelta(long delta, TimeFormat format, Resolution resolution) {
477 if (format == TimeFormat.CALENDAR) {
478 return formatDeltaAbs(delta, resolution);
479 }
480 return formatTime(delta, format, resolution);
481 }
482
483 /**
484 * Formats time delta in ns to Calendar format, only formatting the years,
485 * days, hours or minutes if necessary.
486 *
487 * @param delta
488 * The time delta, in ns
489 * @param resolution
490 * The resolution to use
491 * @return the formatted time delta
492 */
493 public static String formatDeltaAbs(long delta, Resolution resolution) {
494 StringBuffer str = new StringBuffer();
495 if (delta < 0) {
496 str.append('-');
497 }
498 long ns = Math.abs(delta);
499 long seconds = TimeUnit.NANOSECONDS.toSeconds(ns);
500 long minutes = TimeUnit.NANOSECONDS.toMinutes(ns);
501 long hours = TimeUnit.NANOSECONDS.toHours(ns);
502 long days = TimeUnit.NANOSECONDS.toDays(ns);
503 if (days > 0) {
504 str.append(days);
505 str.append("d "); //$NON-NLS-1$
506 }
507 if (hours > 0) {
508 str.append(hours % HOURS_PER_DAY);
509 str.append("h "); //$NON-NLS-1$
510 }
511 if (minutes > 0) {
512 str.append(minutes % MIN_PER_HOUR);
513 str.append("m "); //$NON-NLS-1$
514 }
515 str.append(seconds % SEC_PER_MIN);
516 str.append('.');
517 // append the ms, us and ns as specified in the resolution
518 str.append(formatNs(delta, resolution));
519 str.append("s"); //$NON-NLS-1$
520 return str.toString();
521 }
522
523 /**
524 * Obtains the remainder fraction on unit Seconds of the entered value in
525 * nanoseconds. e.g. input: 1241207054171080214 ns The number of fraction
526 * seconds can be obtained by removing the last 9 digits: 1241207054 the
527 * fractional portion of seconds, expressed in ns is: 171080214
528 *
529 * @param srcTime
530 * The source time in ns
531 * @param res
532 * The Resolution to use
533 * @return the formatted nanosec
534 */
535 public static String formatNs(long srcTime, Resolution res) {
536 StringBuffer str = new StringBuffer();
537 long ns = Math.abs(srcTime % SEC_IN_NS);
538 String nanos = Long.toString(ns);
539 str.append("000000000".substring(nanos.length())); //$NON-NLS-1$
540 str.append(nanos);
541
542 if (res == Resolution.MILLISEC) {
543 return str.substring(0, 3);
544 } else if (res == Resolution.MICROSEC) {
545 return str.substring(0, 6);
546 } else if (res == Resolution.NANOSEC) {
547 return str.substring(0, 9);
548 }
549 return ""; //$NON-NLS-1$
550 }
551
552 /**
553 * FIXME Currently does nothing.
554 *
555 * @param opt
556 * The option name
557 * @param def
558 * The option value
559 * @param min
560 * The minimal accepted value
561 * @param max
562 * The maximal accepted value
563 * @return The value that was read
564 */
565 public static int loadIntOption(String opt, int def, int min, int max) {
566 return def;
567 }
568
569 /**
570 * FIXME currently does nothing
571 *
572 * @param opt
573 * The option name
574 * @param val
575 * The option value
576 */
577 public static void saveIntOption(String opt, int val) {
578 }
579
580 static ITimeEvent getFirstEvent(ITimeGraphEntry entry) {
581 if (null == entry || ! entry.hasTimeEvents()) {
582 return null;
583 }
584 Iterator<? extends ITimeEvent> iterator = entry.getTimeEventsIterator();
585 if (iterator != null && iterator.hasNext()) {
586 return iterator.next();
587 }
588 return null;
589 }
590
591 /**
592 * Gets the {@link ITimeEvent} at the given time from the given
593 * {@link ITimeGraphEntry}.
594 *
595 * @param entry
596 * a {@link ITimeGraphEntry}
597 * @param time
598 * a timestamp
599 * @param n
600 * this parameter means: <list> <li>-1: Previous Event</li> <li>
601 * 0: Current Event</li> <li>
602 * 1: Next Event</li> <li>2: Previous Event when located in a non
603 * Event Area </list>
604 * @return a {@link ITimeEvent}, or <code>null</code>.
605 */
606 public static ITimeEvent findEvent(ITimeGraphEntry entry, long time, int n) {
607 if (null == entry || ! entry.hasTimeEvents()) {
608 return null;
609 }
610 Iterator<@NonNull ? extends ITimeEvent> iterator = entry.getTimeEventsIterator();
611 if (iterator == null) {
612 return null;
613 }
614 ITimeEvent nextEvent = null;
615 ITimeEvent currEvent = null;
616 ITimeEvent prevEvent = null;
617
618 while (iterator.hasNext()) {
619 nextEvent = iterator.next();
620 long nextStartTime = nextEvent.getTime();
621
622 if (nextStartTime > time) {
623 break;
624 }
625
626 if (currEvent == null || currEvent.getTime() != nextStartTime ||
627 (nextStartTime != time && currEvent.getDuration() != nextEvent.getDuration())) {
628 prevEvent = currEvent;
629 currEvent = nextEvent;
630 }
631 }
632
633 if (n == -1) { //previous
634 if (currEvent != null && currEvent.getTime() + currEvent.getDuration() >= time) {
635 return prevEvent;
636 }
637 return currEvent;
638 } else if (n == 0) { //current
639 if (currEvent != null && currEvent.getTime() + currEvent.getDuration() >= time) {
640 return currEvent;
641 }
642 return null;
643 } else if (n == 1) { //next
644 if (nextEvent != null && nextEvent.getTime() > time) {
645 return nextEvent;
646 }
647 return null;
648 } else if (n == 2) { //current or previous when in empty space
649 return currEvent;
650 }
651
652 return null;
653 }
654
655 /**
656 * Pretty-print a method signature.
657 *
658 * @param origSig
659 * The original signature
660 * @return The pretty signature
661 */
662 public static String fixMethodSignature(String origSig) {
663 String sig = origSig;
664 int pos = sig.indexOf('(');
665 if (pos >= 0) {
666 String ret = sig.substring(0, pos);
667 sig = sig.substring(pos);
668 sig = sig + " " + ret; //$NON-NLS-1$
669 }
670 return sig;
671 }
672
673 /**
674 * Restore an original method signature from a pretty-printed one.
675 *
676 * @param ppSig
677 * The pretty-printed signature
678 * @return The original method signature
679 */
680 public static String restoreMethodSignature(String ppSig) {
681 String ret = ""; //$NON-NLS-1$
682 String sig = ppSig;
683
684 int pos = sig.indexOf('(');
685 if (pos >= 0) {
686 ret = sig.substring(0, pos);
687 sig = sig.substring(pos + 1);
688 }
689 pos = sig.indexOf(')');
690 if (pos >= 0) {
691 sig = sig.substring(0, pos);
692 }
693 String args[] = sig.split(","); //$NON-NLS-1$
694 StringBuffer result = new StringBuffer("("); //$NON-NLS-1$
695 for (int i = 0; i < args.length; i++) {
696 String arg = args[i].trim();
697 if (arg.length() == 0 && args.length == 1) {
698 break;
699 }
700 result.append(getTypeSignature(arg));
701 }
702 result.append(")").append(getTypeSignature(ret)); //$NON-NLS-1$
703 return result.toString();
704 }
705
706 /**
707 * Get the mangled type information from an array of types.
708 *
709 * @param typeStr
710 * The types to convert. See method implementation for what it
711 * expects.
712 * @return The mangled string of types
713 */
714 public static String getTypeSignature(String typeStr) {
715 int dim = 0;
716 String type = typeStr;
717 for (int j = 0; j < type.length(); j++) {
718 if (type.charAt(j) == '[') {
719 dim++;
720 }
721 }
722 int pos = type.indexOf('[');
723 if (pos >= 0) {
724 type = type.substring(0, pos);
725 }
726 StringBuffer sig = new StringBuffer(""); //$NON-NLS-1$
727 for (int j = 0; j < dim; j++)
728 {
729 sig.append("["); //$NON-NLS-1$
730 }
731 if (type.equals("boolean")) { //$NON-NLS-1$
732 sig.append('Z');
733 } else if (type.equals("byte")) { //$NON-NLS-1$
734 sig.append('B');
735 } else if (type.equals("char")) { //$NON-NLS-1$
736 sig.append('C');
737 } else if (type.equals("short")) { //$NON-NLS-1$
738 sig.append('S');
739 } else if (type.equals("int")) { //$NON-NLS-1$
740 sig.append('I');
741 } else if (type.equals("long")) { //$NON-NLS-1$
742 sig.append('J');
743 } else if (type.equals("float")) { //$NON-NLS-1$
744 sig.append('F');
745 } else if (type.equals("double")) { //$NON-NLS-1$
746 sig.append('D');
747 } else if (type.equals("void")) { //$NON-NLS-1$
748 sig.append('V');
749 }
750 else {
751 sig.append('L').append(type.replace('.', '/')).append(';');
752 }
753 return sig.toString();
754 }
755
756 /**
757 * Compare two doubles together.
758 *
759 * @param d1
760 * First double
761 * @param d2
762 * Second double
763 * @return 1 if they are different, and 0 if they are *exactly* the same.
764 * Because of the way doubles are stored, it's possible for the
765 * same number obtained in two different ways to actually look
766 * different.
767 */
768 public static int compare(double d1, double d2) {
769 if (d1 > d2) {
770 return 1;
771 }
772 if (d1 < d2) {
773 return 1;
774 }
775 return 0;
776 }
777
778 /**
779 * Compare two character strings alphabetically. This is simply a wrapper
780 * around String.compareToIgnoreCase but that will handle cases where
781 * strings can be null
782 *
783 * @param s1
784 * The first string
785 * @param s2
786 * The second string
787 * @return A number below, equal, or greater than zero if the first string
788 * is smaller, equal, or bigger (alphabetically) than the second
789 * one.
790 */
791 public static int compare(String s1, String s2) {
792 if (s1 != null && s2 != null) {
793 return s1.compareToIgnoreCase(s2);
794 }
795 if (s1 != null) {
796 return 1;
797 }
798 if (s2 != null) {
799 return -1;
800 }
801 return 0;
802 }
803
804 /**
805 * Calculates the square of the distance between two points.
806 *
807 * @param x1
808 * x-coordinate of point 1
809 * @param y1
810 * y-coordinate of point 1
811 * @param x2
812 * x-coordinate of point 2
813 * @param y2
814 * y-coordinate of point 2
815 *
816 * @return the square of the distance in pixels^2
817 */
818 public static double distance2(int x1, int y1, int x2, int y2) {
819 int dx = x2 - x1;
820 int dy = y2 - y1;
821 int d2 = dx * dx + dy * dy;
822 return d2;
823 }
824
825 /**
826 * Calculates the distance between a point and a line segment. If the point
827 * is in the perpendicular region between the segment points, return the
828 * distance from the point to its projection on the segment. Otherwise
829 * return the distance from the point to its closest segment point.
830 *
831 * @param px
832 * x-coordinate of the point
833 * @param py
834 * y-coordinate of the point
835 * @param x1
836 * x-coordinate of segment point 1
837 * @param y1
838 * y-coordinate of segment point 1
839 * @param x2
840 * x-coordinate of segment point 2
841 * @param y2
842 * y-coordinate of segment point 2
843 *
844 * @return the distance in pixels
845 */
846 public static double distance(int px, int py, int x1, int y1, int x2, int y2) {
847 double length2 = distance2(x1, y1, x2, y2);
848 if (length2 == 0) {
849 return Math.sqrt(distance2(px, py, x1, y1));
850 }
851 // 'r' is the ratio of the position, between segment point 1 and segment
852 // point 2, of the projection of the point on the segment
853 double r = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / length2;
854 if (r <= 0.0) {
855 // the projection is before segment point 1, return distance from
856 // the point to segment point 1
857 return Math.sqrt(distance2(px, py, x1, y1));
858 }
859 if (r >= 1.0) {
860 // the projection is after segment point 2, return distance from
861 // the point to segment point 2
862 return Math.sqrt(distance2(px, py, x2, y2));
863 }
864 // the projection is between the segment points, return distance from
865 // the point to its projection on the segment
866 int x = (int) (x1 + r * (x2 - x1));
867 int y = (int) (y1 + r * (y2 - y1));
868 return Math.sqrt(distance2(px, py, x, y));
869 }
870 }
This page took 0.051557 seconds and 4 git commands to generate.