1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.utils.datastructures;
\r
14 import java.io.Serializable;
\r
15 import java.util.ArrayList;
\r
16 import java.util.Arrays;
\r
17 import java.util.Comparator;
\r
18 import java.util.List;
\r
21 * Segment consists of a start and a end value. Both start and end are inclusive.
\r
23 * @See {@link SegmentSet}
\r
24 * @author Toni Kalajainen
\r
26 public final class Segment implements Comparable<Segment>, Serializable, Cloneable {
\r
28 public static final Segment ALL = new Segment(-Double.MAX_VALUE, Double.MAX_VALUE);
\r
29 public static final Segment[] NO_RANGES = new Segment[0];
\r
32 * Define serial version of this class
\r
34 private static final long serialVersionUID = 2979572023736175492L;
\r
37 private double start;
\r
43 * Create a new segment at a single value
\r
45 * @param value value
\r
48 public static Segment at(double value)
\r
50 return new Segment(value, value);
\r
54 * Create the segment between two values
\r
60 public static Segment of(double d1, double d2)
\r
63 return new Segment(d1, d2);
\r
65 return new Segment(d2, d1);
\r
69 * Create a segment. Start and end are included.
\r
70 * Start value must be smaller that end value.
\r
72 * @param start the start time
\r
73 * @param end the end time
\r
75 public Segment(double start, double end) {
\r
77 throw new IllegalArgumentException("Start is after end");
\r
83 * Create single point
\r
85 * @param data Data point
\r
87 public Segment(double data) {
\r
93 * Create as segment.
\r
96 public Segment(double[] data) {
\r
97 assert(data.length ==2);
\r
101 throw new IllegalArgumentException("Start is after end");
\r
108 * @return the end Data
\r
110 public double getEnd() {
\r
115 * Get the start Data
\r
119 public double getStart() {
\r
124 * Get the length of the Data
\r
126 * @return the length
\r
128 public double getLength() {
\r
129 return end - start;
\r
132 public Segment createWithNewStart(double newStartData) {
\r
133 return new Segment(newStartData, end);
\r
136 public Segment createWithNewEnd(double newEndData) {
\r
137 return new Segment(start, newEndData);
\r
141 * Creates a one element array
\r
143 * @return the 1-element array
\r
145 public Segment[] toArray() {
\r
146 Segment[] array = new Segment[1];
\r
152 * Tests wheter this segment contains value
\r
154 * @param value the value
\r
155 * @return true if the value is within the segment
\r
157 public boolean contains(double value) {
\r
158 return (value>=start && value<=end);
\r
162 * Tests whether this segment fully contains s
\r
165 * @return Returns true if this segment contains s
\r
167 public boolean contains(Segment s) {
\r
168 return (start<=s.start && end>=s.end);
\r
172 * Tests whether s intersects or is parallel to this segment.
\r
175 * @return true if the two segments intersect or are parallel
\r
177 public boolean parallelWith(Segment s) {
\r
178 if (s.start > this.end) return false;
\r
179 if (s.end < this.start) return false;
\r
184 * Tests whether s intersects with this segment
\r
187 * @return true if they intersect
\r
189 public boolean intersects(Segment s) {
\r
190 if (s.start >= this.end) return false;
\r
191 if (s.end <= this.start) return false;
\r
196 * Extends the segment with another.
\r
198 * @param s other segment
\r
199 * @return new segment containing both segments
\r
201 public Segment extendWith(Segment s) {
\r
202 if (contains(s)) return this;
\r
203 if (s.contains(this)) return s;
\r
204 double newStart = this.start;
\r
205 if (s.start<newStart) newStart = s.start;
\r
206 double newEnd = this.end;
\r
207 if (s.end>newEnd) newEnd = s.end;
\r
208 return new Segment(newStart, newEnd);
\r
212 * Extends this segment so that value is included.
\r
214 * @param value a value
\r
215 * @return new segment that includes the value
\r
217 public Segment extendWith(double value) {
\r
218 if (this.contains(value)) return this;
\r
219 return new Segment((value<start?value:start),(value>end?value:end));
\r
223 * Create an intersection of this and s
\r
226 * @return the intersection
\r
228 public Segment[] intersection(Segment s) {
\r
229 Segment list[] = new Segment[2];
\r
232 return Segment.intersection(list);
\r
236 * Create an union of this and s
\r
239 * @return the union
\r
241 public Segment[] union(Segment range) {
\r
242 Segment list[] = new Segment[2];
\r
245 return Segment.union(list);
\r
249 * Create intersection
\r
251 * @param segments1 group of Data range
\r
252 * @param segments2 group of Data range
\r
253 * @return the intersection
\r
255 public static Segment[] intersection(Segment segments1[],
\r
256 Segment segments2[]) {
\r
257 return intersection(concat(segments1, segments2));
\r
263 * @param segments1 group of segments
\r
264 * @param segments2 group of segments
\r
265 * @return the union
\r
267 public static Segment[] union(Segment segments1[], Segment segments2[]) {
\r
268 return union(concat(segments1, segments2));
\r
272 * Returns difference of segments
\r
278 public static Segment[] difference(Segment group[], Segment cutWith) {
\r
279 if (group.length==0) return group;
\r
281 List<Segment> result = new ArrayList<Segment>(group.length+2);
\r
282 for (Segment range : group) {
\r
283 // Cuts the range completely
\r
284 if (cutWith.start<=range.start && cutWith.end>=range.end) continue;
\r
285 // Doesn't cut the range at all
\r
286 if (cutWith.start>=range.end || cutWith.end<=range.start) {
\r
290 // Splits the range in half
\r
291 if (cutWith.start>range.start && cutWith.end<range.end) {
\r
292 result.add(new Segment(range.start, cutWith.start));
\r
293 result.add(new Segment(cutWith.end, range.end));
\r
296 // cuts the range from one end
\r
297 if (cutWith.start<=range.start) {
\r
298 result.add(new Segment(cutWith.end, range.end));
\r
301 // cuts the range from one end
\r
302 if (cutWith.end>=range.end) {
\r
303 result.add(new Segment(range.start, cutWith.start));
\r
309 return (Segment[]) result.toArray(NO_RANGES);
\r
313 * Returns difference of segments
\r
318 public static Segment[] difference(Segment segments1[], Segment segments2[]) {
\r
319 // TODO : this could be optimized (both arrays are sorted)
\r
320 Segment temp[] = segments1;
\r
321 for (Segment s : segments2)
\r
322 temp = difference(temp, s);
\r
329 * @param array1 array
\r
330 * @param array2 array
\r
331 * @return the concatenation
\r
333 public static Segment[] concat(Segment array1[], Segment array2[]) {
\r
334 Segment result[] = new Segment[array1.length + array2.length];
\r
335 System.arraycopy(array1, 0, result, 0, array1.length);
\r
336 System.arraycopy(array2, 0, result, array1.length, array2.length);
\r
341 * Create an intersection with another segment
\r
344 * @return null if no intersection, otherwise the intersection
\r
346 Segment intersectWith(Segment s) {
\r
347 if (!intersects(s)) return null;
\r
348 double newStart = this.start;
\r
349 if (s.start>newStart) newStart = s.start;
\r
350 double newEnd = this.end;
\r
351 if (s.end<newEnd) newEnd = s.end;
\r
352 return new Segment(newStart, newEnd);
\r
356 * Create an difference with another segment
\r
359 * @return null if no intersection, otherwise the intersection
\r
361 Segment differenceWith(Segment s) {
\r
362 if (!intersects(s)) return this;
\r
363 double newStart = this.start;
\r
364 double newEnd = this.end;
\r
365 if (s.start<newEnd) newEnd = s.start;
\r
366 if (s.end>newStart) newStart = s.end;
\r
367 if (newEnd < newStart)
\r
369 return new Segment(newStart, newEnd);
\r
372 * Intersect a group of segments.
\r
373 * In the result contains all segments where 2 or more segments intersected
\r
374 * in the source group.
\r
376 * @param group group of segments
\r
377 * @return intersection of segments. sorted array
\r
379 public static Segment[] intersection(Segment group[]) {
\r
380 group = sort(group);
\r
382 Segment[] temp = new Segment[group.length];
\r
384 Segment base = group[0];
\r
385 for (int i=1; i<group.length; i++) {
\r
386 Segment r = group[i];
\r
388 Segment is = base.intersectWith(r);
\r
389 if (is != null) temp[count++] = is;
\r
392 if (r.parallelWith(base)) {
\r
393 base = base.extendWith(r);
\r
395 // Choose a new base
\r
399 return resizeArray(temp, count);
\r
403 * Create an union a group of segments
\r
405 * @param group group of segments
\r
406 * @return incrementally sorted, non-intersecting array of segments
\r
408 public static Segment[] union(Segment group[]) {
\r
409 if (group.length<=1) return group;
\r
410 group = sort(group);
\r
413 Segment[] temp = new Segment[group.length];
\r
415 Segment base = group[0];
\r
416 for (int i=1; i<group.length; i++) {
\r
417 Segment r = group[i];
\r
418 if (r.parallelWith(base))
\r
419 base = base.extendWith(r);
\r
421 temp[count++] = base;
\r
425 temp[count++] = base;
\r
426 return resizeArray(temp, count);
\r
432 * @param array the array
\r
433 * @param newLength new length of the array
\r
434 * @return new resized array
\r
436 public static Segment[] resizeArray(Segment array[], int newLength) {
\r
437 int oldLength = array.length;
\r
438 if (oldLength==newLength) return array;
\r
440 Segment newArray[] = new Segment[newLength];
\r
442 if (newLength<oldLength)
\r
443 System.arraycopy(array, 0, newArray, 0, newLength);
\r
445 System.arraycopy(array, 0, newArray, 0, oldLength);
\r
450 * Sort array by their start Datas
\r
452 * @param group group of segments to sort
\r
453 * @return incrementally sorted array of segments
\r
455 public static Segment[] sort(Segment group[]) {
\r
456 Segment[] result = (Segment[]) group.clone();
\r
457 Arrays.sort(result, COMPARATOR);
\r
461 private static final Comparator<Segment> COMPARATOR = new Comparator<Segment>() {
\r
462 public int compare(Segment o1, Segment o2) {
\r
463 if (o1.start < o2.start)
\r
465 if (o1.start > o2.start)
\r
468 if (o1.end < o2.end)
\r
470 if (o1.end > o2.end)
\r
478 public int hashCode() {
\r
479 long bits = Double.doubleToLongBits(start);
\r
480 int hash = (int)(bits ^ (bits >>> 32));
\r
481 bits = Double.doubleToLongBits(end);
\r
482 hash ^= (int)(bits ^ (bits >>> 32));
\r
486 public boolean equals(Object obj) {
\r
487 if (!(obj instanceof Segment)) return false;
\r
488 Segment other = (Segment) obj;
\r
489 return (other.end == this.end) && (other.start == this.start);
\r
493 * Segment start are compared. If they are equal, ends are compared.
\r
494 * Smallest value first.
\r
496 * @param other compare to
\r
497 * @return the compare value
\r
499 public int compareTo(Segment other) {
\r
500 double diffrence = this.start - other.start;
\r
506 diffrence = this.end - other.end;
\r
515 public Segment clone() {
\r
516 return new Segment(start, end);
\r
519 public String toString() {
\r
520 return "["+start+".."+end+"]";
\r
523 public static String toString(Segment array[]) {
\r
524 return Arrays.toString(array);
\r