]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/SegmentSet.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / SegmentSet.java
diff --git a/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/SegmentSet.java b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/SegmentSet.java
new file mode 100644 (file)
index 0000000..e722d4a
--- /dev/null
@@ -0,0 +1,358 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.utils.datastructures;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+/**\r
+ * SegmentSet is a set of value segments.\r
+ * \r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class SegmentSet implements Iterable<Segment> {\r
+\r
+       public static final SegmentSet NONE = new SegmentSet();\r
+       \r
+       /** incrementally sorted and non-intersecting, non-parallel array of segments */\r
+       List<Segment> list = new ArrayList<Segment>();\r
+       \r
+       /**\r
+        * Create new segment set from double value pairs \r
+        * \r
+        * @param times even length array\r
+        * @return\r
+        */\r
+       public static SegmentSet of(double...times)\r
+       {\r
+               if (times==null || ((times.length & 1)==1))\r
+                       throw new IllegalArgumentException();\r
+               SegmentSet result = new SegmentSet();\r
+               for (int i=0; i<times.length/2; i++)\r
+                       result.add( Segment.of(times[i*2], times[i*2+1]) );\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Create new set of ranges.\r
+        */\r
+       public SegmentSet() {           \r
+       }\r
+\r
+       /**\r
+        * Create new set of ranges.\r
+        */\r
+       public SegmentSet(SegmentSet copyFrom) {\r
+               this.list.addAll(copyFrom.list);\r
+       }\r
+       \r
+       /**\r
+        * Create new set of segments.\r
+        */\r
+       public SegmentSet(Collection<Segment> list) {\r
+               for (Segment r : list)\r
+                       add(r);\r
+       }\r
+       \r
+       /**\r
+        * Create new set of segments.\r
+        * \r
+        * @param segments initial segments\r
+        */\r
+       public SegmentSet(Segment...segments) {\r
+               for (Segment r : Segment.union(segments))\r
+                       list.add(r);\r
+       }\r
+       \r
+       public boolean add(Segment...segments) {\r
+               boolean result = false;\r
+               for (Segment r : segments) \r
+                       result |= add(r);\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Add a segment set to this segment set\r
+        *  \r
+        * @param set \r
+        * @return true if the content changed\r
+        */\r
+       public boolean add(SegmentSet set) {\r
+               boolean result = false;\r
+               for (Segment r : set.list)\r
+                       result |= add(r);\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Get index of the segment that contains the given value.\r
+        * If value is not included in the set, return as negative value the index\r
+        * of the range the value is prior to.\r
+        *  e.g. \r
+        *    -1 before 0\r
+        *    -2 before 1\r
+        * \r
+        * @return range index\r
+        */\r
+       int indexOf(double value) {\r
+               for (int i=0; i<list.size(); i++) {\r
+                       Segment r = list.get(i);\r
+                       if (r.contains(value)) return i;\r
+                       if (r.getStart()>=value) return -i -1;\r
+               }\r
+               return -list.size()-1;\r
+       }\r
+       \r
+       /**\r
+        * Get the segment the value is in.\r
+        * \r
+        * @param value \r
+        * @return segment or null\r
+        */\r
+       public Segment getSegmentOf(double value)\r
+       {\r
+               int index = indexOf(value);\r
+               if (index<0) return null;\r
+               return list.get(index);\r
+       }\r
+       \r
+       /**\r
+        * Add segment range\r
+        * \r
+        * @param range\r
+        * @return true if the content changed\r
+        */\r
+       public boolean add(Segment range) {\r
+               int startIndex = indexOf(range.getStart());\r
+               int endIndex = indexOf(range.getEnd());\r
+                               \r
+               // The whole segment is already included in the set\r
+               if (startIndex>=0 && startIndex==endIndex) return false;\r
+               \r
+               // Doesn't intersect, add as is\r
+               if (startIndex<0 && startIndex==endIndex) {\r
+                       list.add(-startIndex-1, range);\r
+                       return true;\r
+               }\r
+\r
+               // Intersects partially\r
+               double start = range.getStart();\r
+               double end = range.getEnd();\r
+\r
+               // Extend the segment following the start position \r
+               if (startIndex<0) {\r
+                       startIndex = -startIndex-1;\r
+               } else {\r
+                       start = list.get(startIndex).getStart();\r
+               }\r
+               \r
+               // Extends the segment preceding the end position \r
+               if (endIndex<0) {\r
+                       endIndex = -endIndex-2;\r
+               } else {\r
+                       end = list.get(endIndex).getEnd();\r
+               }\r
+                               \r
+               int segmentsToRemove = endIndex - startIndex;\r
+               for (int j=0; j<segmentsToRemove; j++)\r
+                       list.remove(startIndex);\r
+               \r
+               if (start!=range.getStart() || end!=range.getEnd())\r
+                       range = new Segment(start, end);\r
+               \r
+               list.set( startIndex, range );          \r
+               return true;\r
+       }\r
+       \r
+       public boolean remove(Segment...ranges) {\r
+               boolean result = false;\r
+               for (Segment r : ranges)\r
+                       result |= remove(r);\r
+               return result;\r
+       }\r
+       \r
+       public boolean remove(SegmentSet set) {\r
+               boolean result = false;\r
+               for (Segment r : set.list)\r
+                       result |= remove(r);\r
+               return result;\r
+       }\r
+       \r
+       public boolean remove(Segment range) {\r
+               int startIndex = indexOf(range.getStart());\r
+               int endIndex = indexOf(range.getEnd());\r
+\r
+               // Doesn't intersect\r
+               if (startIndex<0 && startIndex==endIndex) return false;\r
+\r
+               // Move start to next segment\r
+               if (startIndex<0) \r
+                       startIndex = -startIndex-1;\r
+               \r
+               // Move end to prev segment\r
+               if (endIndex<0) \r
+                       endIndex = -endIndex-2;\r
+\r
+               // Start position is in segment. Cut segment and move to next one\r
+               if (range.getStart()>list.get(startIndex).getStart()) {\r
+                       Segment s = list.get(startIndex);\r
+                       Segment newS = new Segment(s.getStart(), range.getStart());\r
+                       list.set(startIndex, newS);\r
+                       startIndex++;\r
+                       \r
+                       if (s.getEnd()>range.getEnd()) {\r
+                               newS = new Segment(range.getEnd(), s.getEnd());\r
+                               list.add(startIndex, newS);\r
+                               startIndex++;\r
+                       }                       \r
+               }\r
+               \r
+               // End position is within segment, Cut it and move to prev one\r
+               if (range.getEnd()<list.get(endIndex).getEnd()) {\r
+                       Segment s = list.get(endIndex);\r
+                       s = new Segment(range.getEnd(), s.getEnd());\r
+                       list.set(endIndex, s);\r
+                       endIndex--;\r
+               }\r
+               \r
+               // Remove start if it is cut completely\r
+               while ( startIndex<=endIndex && range.contains( list.get(startIndex) ) ) {\r
+                       list.remove(startIndex);\r
+                       endIndex--;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * Clips SegmentSet to given segment.\r
+        * @param segment\r
+        */\r
+       public void clip(Segment segment) {\r
+               remove(new Segment(-Double.MAX_VALUE,segment.getStart()));\r
+               remove(new Segment(segment.getEnd(),Double.MAX_VALUE));\r
+       }\r
+\r
+       /**\r
+        * Tests whether s intersects \r
+        * \r
+        * @param s segmentset\r
+        * @return true if s intersects with this segment set \r
+        */\r
+       public boolean intersects(SegmentSet set)\r
+       {\r
+               for (Segment s : set.list)\r
+                       if (intersects(s)) return true;\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Tests whether s intersects \r
+        * \r
+        * @param s segment\r
+        * @return true if s intersects with this segment set \r
+        */\r
+       public boolean intersects(Segment s)\r
+       {\r
+               for (Segment localS : list)\r
+                       if (localS.intersects(s)) return true;\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Tests whether s is fully contained in this segment set. \r
+        * \r
+        * @param s segment\r
+        * @return true if s is fully contained in this segment set\r
+        */\r
+       public boolean contains(Segment s) \r
+       {\r
+               for (Segment localS : list)\r
+                       if (localS.contains(s)) return true;\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Tests whether the a values is included in the set.\r
+        *\r
+        * @param value\r
+        * @return\r
+        */\r
+       public boolean contains(double value)\r
+       {\r
+               return indexOf(value)>=0;\r
+       }\r
+       \r
+       /**\r
+        * Get the min and max value\r
+        * \r
+        * @return boundaries or null\r
+        */\r
+       public Segment getBoundaries()\r
+       {\r
+               if (list.isEmpty()) return null;\r
+               double start = list.get(0).getStart();\r
+               double end = list.get(list.size()-1).getEnd();\r
+               return new Segment(start, end);\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @return lower bound or null\r
+        */\r
+       public Double getLowerBound() {\r
+               if (list.isEmpty()) return null;\r
+               return list.get(0).getStart();\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @return upper bound or null\r
+        */\r
+       public Double getUpperBound() {\r
+               if (list.isEmpty()) return null;\r
+               return list.get(list.size()-1).getEnd();\r
+       }\r
+       \r
+       /**\r
+        * Return incrementally sorted and non-intersecting array of segments.\r
+        * \r
+        * Note this method returns an internal state, it most not be modified.\r
+        * \r
+        * @return array\r
+        */\r
+       public Segment[] toArray() {\r
+               return list.toArray( new Segment[list.size()] );\r
+       }\r
+       \r
+    /**\r
+     * Returns an iterator over the elements in this list in proper sequence.\r
+     *\r
+     * @return an iterator over the elements in this list in proper sequence.\r
+     */\r
+    public Iterator<Segment> iterator() {\r
+       return list.iterator();\r
+    }  \r
+       \r
+       @Override\r
+       public String toString() {\r
+               return Arrays.toString( toArray() );\r
+       }\r
+       \r
+       public SegmentSet clone() {\r
+               return new SegmentSet(this);\r
+       }\r
+       \r
+}\r
+\r