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