2 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
\r
3 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
4 of this software and associated documentation files (the "Software"), to deal
\r
5 in the Software without restriction, including without limitation the rights
\r
6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
7 copies of the Software, and to permit persons to whom the Software is
\r
8 furnished to do so, subject to the following conditions:
\r
10 The above copyright notice and this permission notice shall be included in
\r
11 all copies or substantial portions of the Software.
\r
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
23 using System.Diagnostics;
\r
25 using SCG = System.Collections.Generic;
\r
27 namespace PointLocation
\r
29 //public enum Site { Cell,Edge,Outside}
\r
31 /// A line segment with associated data of type T for the cell
\r
32 /// to its right respectively left.
\r
34 public struct Edge<T>
\r
36 public double xs, ys, xe, ye;
\r
38 public T right, left;
\r
40 public Edge(double xs, double ys, double xe, double ye, T right, T left)
\r
51 public T Cell(bool upper)
\r
53 return (DoubleComparer.StaticCompare(xs, xe) < 0) == upper ? left : right;
\r
57 public override string ToString()
\r
59 return String.Format("[({0:G5};{1:G5})->({2:G5};{3:G5})/R:{4} L:{5}]", xs, ys, xe, ye, right, left);
\r
66 /// A data structure for point location in a plane divided into
\r
67 /// cells by edges. This is the classical use of persistent trees
\r
68 /// by Sarnak and Tarjan [?]. See de Berg et al for alternatives.
\r
70 /// The internal data is an outer sorted dictionary that maps each
\r
71 /// x coordinate of an endpoint of some edge to an inner sorted set
\r
72 /// of the edges crossing or touching the vertical line at that x
\r
73 /// coordinate, the edges being ordered by their y coordinates
\r
74 /// to the immediate right of x. Lookup of a point (x,y) is done by
\r
75 /// finding the predecessor of x cell the outer dictionary and then locating
\r
76 /// the edges above and below (x,y) by searching in the inner sorted set.
\r
78 /// The creation of the inner sorted sets is done by maintaining a
\r
79 /// (persistent) tree of edges, inserting and deleting edges according
\r
80 /// to a horzontal sweep of the edges while saving a snapshot of the
\r
81 /// inner tree in the outer dictionary at each new x coordinate.
\r
83 /// If there are n edges, there will be 2n updates to the inner tree,
\r
84 /// and in the worst case, the inner tree will have size Omega(n) for
\r
85 /// Omega(n) snapshots. We will use O(n*logn) time and O(n) space for
\r
86 /// sorting the endpoints. If we use a nodecopying persistent inner tree,
\r
87 /// we will use O(n) space and time for building the data structure proper.
\r
88 /// If we use a pathcopy persistent tree, we will use O(n*logn) time and
\r
89 /// space for the data struicture. Finally, if we use a non-persistent
\r
90 /// tree with a full copy for snapshot, we may use up to O(n^2) space and
\r
91 /// time for building the datastructure.
\r
93 /// Lookup will take O(logn) time in any case, but taking the memory
\r
94 /// hierarchy into consideration, a low space use is very beneficial
\r
95 /// for large problems.
\r
97 /// The code assumes that the given set of edges is correct, in particular
\r
98 /// that they do not touch at interior points (e.g. cross or coincide).
\r
101 public class PointLocator<T>
\r
103 private TreeDictionary<double,ISorted<Edge<T>>> htree;
\r
105 private EdgeComparer<T> lc = new EdgeComparer<T>();
\r
107 private SCG.IComparer<EndPoint> epc = new EndPoint(0, 0, true, 0);
\r
109 private DoubleComparer dc = new DoubleComparer();
\r
111 private TreeDictionary<EndPoint,Edge<T>> endpoints;
\r
115 private bool built = false;
\r
117 public PointLocator()
\r
119 //htree = new TreeDictionary<double,TreeSet<Edge<T>>>(dc);
\r
120 endpoints = new TreeDictionary<EndPoint,Edge<T>>(epc);
\r
123 public PointLocator(SCG.IEnumerable<Edge<T>> edges)
\r
125 //htree = new TreeDictionary<double,TreeSet<Edge<T>>>(dc);
\r
126 endpoints = new TreeDictionary<EndPoint,Edge<T>>(epc);
\r
127 foreach (Edge<T> edge in edges)
\r
131 private void add(Edge<T> edge)
\r
133 int c = DoubleComparer.StaticCompare(edge.xs, edge.xe);
\r
138 endpoints.Add(new EndPoint(edge.xs, edge.ys, c < 0, count), edge);
\r
139 endpoints.Add(new EndPoint(edge.xe, edge.ye, c > 0, count++), edge);
\r
142 public void Add(Edge<T> edge)
\r
145 throw new InvalidOperationException("PointLocator static when built");
\r
149 public void AddAll(SCG.IEnumerable<Edge<T>> edges)
\r
152 throw new InvalidOperationException("PointLocator static when built");
\r
154 foreach (Edge<T> edge in edges)
\r
158 public void Build()
\r
161 htree = new TreeDictionary<double,ISorted<Edge<T>>>(dc);
\r
163 TreeSet<Edge<T>> vtree = new TreeSet<Edge<T>>(lc);
\r
164 double lastx = Double.NegativeInfinity;
\r
166 foreach (KeyValuePair<EndPoint,Edge<T>> p in endpoints)
\r
168 if (dc.Compare(p.Key.x,lastx)>0)
\r
170 //Put an empty snapshot at -infinity!
\r
171 htree[lastx] = (ISorted<Edge<T>>)(vtree.Snapshot());
\r
172 lc.X = lastx = p.Key.x;
\r
173 lc.compareToRight = false;
\r
178 if (!lc.compareToRight)
\r
179 lc.compareToRight = true;
\r
180 Debug.Assert(vtree.Check());
\r
181 bool chk = vtree.Add(p.Value);
\r
182 Debug.Assert(vtree.Check());
\r
184 Debug.Assert(chk,"edge was not added!",""+p.Value);
\r
188 Debug.Assert(!lc.compareToRight);
\r
190 Debug.Assert(vtree.Check("C"));
\r
192 bool chk = vtree.Remove(p.Value);
\r
193 Debug.Assert(vtree.Check("D"));
\r
195 Debug.Assert(chk,"edge was not removed!",""+p.Value);
\r
198 lc.compareToRight = true;
\r
200 htree[lastx] = (TreeSet<Edge<T>>)(vtree.Snapshot());
\r
205 /*public void Clear()
\r
211 /// Find the cell, if any, containing (x,y).
\r
213 /// <param name="x">x coordinate of point</param>
\r
214 /// <param name="y">y coordinate of point</param>
\r
215 /// <param name="below">Associate data of cell according to edge below</param>
\r
216 /// <param name="above">Associate data of cell according to edge above</param>
\r
217 /// <returns>True if point is inside some cell</returns>
\r
218 public bool Place(double x, double y, out T cell)
\r
221 throw new InvalidOperationException("PointLocator must be built first");
\r
223 KeyValuePair<double,ISorted<Edge<T>>> p = htree.WeakPredecessor(x);
\r
225 //if (DoubleComparer.StaticCompare(cell.key,x)==0)
\r
226 //Just note it, we have thrown away the vertical edges!
\r
229 PointComparer<T> c = new PointComparer<T>(x, y);
\r
231 //Return value true here means we are at an edge.
\r
232 //But note that if x is in htree.Keys, we may be at a
\r
233 //vertical edge even if the return value is false here.
\r
234 //Therefore we do not attempt to sort out completely the case
\r
235 //where (x,y) is on an edge or even on several edges,
\r
236 //and just deliver some cell it is in.
\r
237 p.Value.Cut(c, out low, out lval, out high, out hval);
\r
238 if (!lval || !hval)
\r
245 cell = low.Cell(true);//high.Cell(false);
\r
250 public void Place(double x, double y, out T upper, out bool hval, out T lower, out bool lval)
\r
253 throw new InvalidOperationException("PointLocator must be built first");
\r
255 KeyValuePair<double,ISorted<Edge<T>>> p = htree.WeakPredecessor(x);
\r
257 //if (DoubleComparer.StaticCompare(cell.key,x)==0)
\r
258 //Just note it, we have thrown away the vertical edges!
\r
260 PointComparer<T> c = new PointComparer<T>(x, y);
\r
262 //Return value true here means we are at an edge.
\r
263 //But note that if x is in htree.Keys, we may be at a
\r
264 //vertical edge even if the return value is false here.
\r
265 //Therefore we do not attempt to sort out completely the case
\r
266 //where (x,y) is on an edge or even on several edges,
\r
267 //and just deliver some cell it is in.
\r
268 p.Value.Cut(c, out low, out lval, out high, out hval);
\r
269 upper = hval ? high.Cell(false) : default(T);
\r
270 lower = lval ? low.Cell(true) : default(T);
\r
274 public void Test(double x, double y)
\r
278 if (Place(x, y, out cell))
\r
279 Console.WriteLine("({0}; {1}): <- {2} ", x, y, cell);
\r
281 Console.WriteLine("({0}; {1}): -", x, y);
\r
285 /// Endpoint of an edge with ordering/comparison according to x
\r
286 /// coordinates with arbitration by the id field.
\r
287 /// The client is assumed to keep the ids unique.
\r
289 public /*private*/ struct EndPoint: SCG.IComparer<EndPoint>
\r
291 public double x, y;
\r
298 public EndPoint(double x, double y, bool left, int id)
\r
300 this.x = x;this.y = y;this.start = left;this.id = id;
\r
304 public int Compare(EndPoint a, EndPoint b)
\r
306 int c = DoubleComparer.StaticCompare(a.x, b.x);
\r
308 return c != 0 ? c : (a.start && !b.start) ? 1 : (!a.start && b.start) ? -1 : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
\r
314 /// Compare two doubles with tolerance.
\r
316 class DoubleComparer: SCG.IComparer<double>
\r
318 private const double eps = 1E-10;
\r
320 public int Compare(double a, double b)
\r
322 return a > b + eps ? 1 : a < b - eps ? -1 : 0;
\r
325 public static int StaticCompare(double a, double b)
\r
327 return a > b + eps ? 1 : a < b - eps ? -1 : 0;
\r
332 /// Compare a given point (x,y) to edges: is the point above, at or below
\r
333 /// the edge. Assumes edges not vertical.
\r
335 class PointComparer<T>: IComparable<Edge<T>>
\r
337 private double x, y;
\r
339 public PointComparer(double x, double y)
\r
341 this.x = x; this.y = y;
\r
344 public int CompareTo(Edge<T> a)
\r
346 double ya = (a.ye - a.ys) / (a.xe - a.xs) * (x - a.xs) + a.ys;
\r
348 return DoubleComparer.StaticCompare(y, ya);
\r
351 public bool Equals(Edge<T> a) { return CompareTo(a) == 0; }
\r
355 /// Compare two edges at a given x coordinate:
\r
356 /// Compares the y-coordinate to the immediate right of x of the two edges.
\r
357 /// Assumes edges to be compared are not vertical.
\r
359 class EdgeComparer<T>: SCG.IComparer<Edge<T>>
\r
363 public bool compareToRight = true;
\r
365 public double X { get { return x; } set { x = value; } }
\r
367 public int Compare(Edge<T> line1, Edge<T> line2)
\r
369 double a1 = (line1.ye - line1.ys) / (line1.xe - line1.xs);
\r
370 double a2 = (line2.ye - line2.ys) / (line2.xe - line2.xs);
\r
371 double ya = a1 * (x - line1.xs) + line1.ys;
\r
372 double yb = a2 * (x - line2.xs) + line2.ys;
\r
373 int c = DoubleComparer.StaticCompare(ya, yb);
\r
375 return c != 0 ? c : (compareToRight ? 1 : -1) * DoubleComparer.StaticCompare(a1, a2);
\r
381 public class Ugly : EnumerableBase<Edge<int>>, SCG.IEnumerable<Edge<int>>, SCG.IEnumerator<Edge<int>>
\r
383 private int level = -1, maxlevel;
\r
385 private bool leftend = false;
\r
387 public Ugly(int maxlevel)
\r
389 this.maxlevel = maxlevel;
\r
392 public override SCG.IEnumerator<Edge<int>> GetEnumerator()
\r
394 return (SCG.IEnumerator<Edge<int>>)MemberwiseClone();
\r
397 public void Reset()
\r
403 public bool MoveNext()
\r
405 if (level > maxlevel)
\r
406 throw new InvalidOperationException();
\r
416 return ++level <= maxlevel;
\r
420 public Edge<int> Current
\r
424 if (level < 0 || level > maxlevel)
\r
425 throw new InvalidOperationException();
\r
427 double y = (level * 37) % maxlevel;
\r
428 double deltax = leftend ? 1 : maxlevel;
\r
431 return new Edge<int>(0, y, level, y - 0.5, 0, 0);
\r
433 return new Edge<int>(level, y - 0.5, level, y, 0, 0);
\r
438 public void Dispose() { }
\r
440 #region IEnumerable Members
\r
442 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
\r
444 throw new Exception("The method or operation is not implemented.");
\r
449 #region IEnumerator Members
\r
451 object System.Collections.IEnumerator.Current
\r
453 get { throw new Exception("The method or operation is not implemented."); }
\r
456 void System.Collections.IEnumerator.Reset()
\r
458 throw new Exception("The method or operation is not implemented.");
\r
464 public class TestUgly
\r
470 private PointLocator<int> pointlocator;
\r
473 public TestUgly(int d)
\r
476 ugly = new Ugly(d);
\r
480 public double Traverse()
\r
484 foreach (Edge<int> e in ugly) xsum += e.xe;
\r
489 public bool LookUp(int count, int seed)
\r
491 Random random = new Random(seed);
\r
494 for (int i = 0; i < count; i++)
\r
498 res ^= pointlocator.Place(random.NextDouble() * d, random.NextDouble() * d, out cell);
\r
504 public static void Run(string[] args)
\r
506 int d = args.Length >= 2 ? int.Parse(args[1]) : 400;//00;
\r
507 int repeats = args.Length >= 3 ? int.Parse(args[2]) : 10;
\r
508 int lookups = args.Length >= 4 ? int.Parse(args[3]) : 500;//00;
\r
510 new TestUgly(d).run(lookups);
\r
514 public void run(int lookups)
\r
520 pointlocator = new PointLocator<int>(ugly);
\r
521 pointlocator.Build();
\r
523 LookUp(lookups, 567);
\r
527 public class Lattice : EnumerableBase<Edge<string>>, SCG.IEnumerable<Edge<string>>, SCG.IEnumerator<Edge<string>>, System.Collections.IEnumerator
\r
529 private int currenti = -1, currentj = 0, currentid = 0;
\r
531 private bool currenthoriz = true;
\r
533 private int maxi, maxj;
\r
535 private double a11 = 1, a21 = -1, a12 = 1, a22 = 1;
\r
537 public Lattice(int maxi, int maxj, double a11, double a21, double a12, double a22)
\r
547 public Lattice(int maxi, int maxj)
\r
553 public override SCG.IEnumerator<Edge<string>> GetEnumerator()
\r
555 return (SCG.IEnumerator<Edge<string>>)MemberwiseClone();
\r
558 public void Reset()
\r
563 currenthoriz = true;
\r
566 public bool MoveNext()
\r
571 if (++currenti >= maxi)
\r
573 if (currentj >= maxj)
\r
577 currenthoriz = false;
\r
584 if (++currenti > maxi)
\r
587 currenthoriz = true;
\r
588 if (++currentj > maxj)
\r
597 private string i2l(int i)
\r
601 do { ls++; j = j / 26 - 1; } while (j >= 0);
\r
603 char[] res = new char[ls];
\r
605 while (ls > 0) { res[--ls] = (char)(65 + i % 26); i = i / 26 - 1; }
\r
608 return new String(res);
\r
612 private string fmtid(int i, int j)
\r
614 return "";//cell + ";" + cell;
\r
615 /*if (cell < 0 || cell < 0 || cell >= maxi || cell >= maxj)
\r
618 return String.Format("{0}{1}", i2l(cell), cell);*/
\r
622 public Edge<string> Current
\r
626 if (currenti >= maxi && currentj >= maxj)
\r
627 throw new InvalidOperationException();
\r
629 double xs = currenti * a11 + currentj * a12;
\r
630 double ys = currenti * a21 + currentj * a22;
\r
631 double deltax = currenthoriz ? a11 : a12;
\r
632 double deltay = currenthoriz ? a21 : a22;
\r
633 string r = fmtid(currenti, currenthoriz ? currentj - 1 : currentj);
\r
634 string l = fmtid(currenthoriz ? currenti : currenti - 1, currentj);
\r
636 return new Edge<string>(xs, ys, xs + deltax, ys + deltay, r, l);
\r
641 public void Dispose() { }
\r
643 #region IEnumerable Members
\r
645 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
\r
647 throw new Exception("The method or operation is not implemented.");
\r
652 #region IEnumerator Members
\r
654 object System.Collections.IEnumerator.Current
\r
656 get { throw new Exception("The method or operation is not implemented."); }
\r
659 bool System.Collections.IEnumerator.MoveNext()
\r
661 throw new Exception("The method or operation is not implemented.");
\r
664 void System.Collections.IEnumerator.Reset()
\r
666 throw new Exception("The method or operation is not implemented.");
\r
672 public class TestLattice
\r
674 private Lattice lattice;
\r
678 private PointLocator<string> pointlocator;
\r
681 public TestLattice(int d)
\r
684 lattice = new Lattice(d, d, 1, 0, 0, 1);
\r
687 public TestLattice(int d, double shear)
\r
690 lattice = new Lattice(d, d, 1, 0, shear, 1);
\r
693 public double Traverse()
\r
697 foreach (Edge<string> e in lattice) xsum += e.xe;
\r
703 public bool LookUp(int count, int seed)
\r
705 Random random = new Random(seed);
\r
708 for (int i = 0; i < count; i++)
\r
712 res ^= pointlocator.Place(random.NextDouble() * d, random.NextDouble() * d, out cell);
\r
719 public static void Run()
\r
723 int lookups = 50000;
\r
724 TestLattice tl = null;
\r
726 Console.WriteLine("TestLattice Run({0}), means over {1} repeats:", d, repeats);
\r
727 tl = new TestLattice(d, 0.000001);
\r
731 tl.pointlocator = new PointLocator<string>();
\r
733 tl.pointlocator.AddAll(tl.lattice);
\r
735 tl.pointlocator.Build();
\r
737 tl.LookUp(lookups, 567);
\r
741 public void BasicRun()
\r
743 pointlocator.Test(-0.5, -0.5);
\r
744 pointlocator.Test(-0.5, 0.5);
\r
745 pointlocator.Test(-0.5, 1.5);
\r
746 pointlocator.Test(0.5, -0.5);
\r
747 pointlocator.Test(0.5, 0.5);
\r
748 pointlocator.Test(0.5, 1.5);
\r
749 pointlocator.Test(1.5, -0.5);
\r
750 pointlocator.Test(1.5, 0.5);
\r
751 pointlocator.Test(1.5, 1.5);
\r
752 pointlocator.Test(1.5, 4.99);
\r
753 pointlocator.Test(1.5, 5);
\r
754 pointlocator.Test(1.5, 5.01);
\r
755 pointlocator.Test(1.99, 4.99);
\r
756 pointlocator.Test(1.99, 5);
\r
757 pointlocator.Test(1.99, 5.01);
\r
758 pointlocator.Test(2, 4.99);
\r
759 pointlocator.Test(2, 5);
\r
760 pointlocator.Test(2, 5.01);
\r
761 pointlocator.Test(2.01, 4.99);
\r
762 pointlocator.Test(2.01, 5);
\r
763 pointlocator.Test(2.01, 5.01);
\r
768 public class TestPointLocation {
\r
769 public static void Main(String[] args) {
\r
770 Test.TestUgly.Run(new String[0]);
\r