UnityMol  0.9.6-875
UnityMol viewer / In developement
FortuneVoronoi.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections;
3 using BenTools.Data;
4 using UnityEngine;
5 
6 namespace BenTools.Mathematics
7 {
8  public class VoronoiGraph
9  {
10  public HashSet Vertizes = new HashSet();
11  public HashSet Edges = new HashSet();
12  }
13  public class VoronoiEdge
14  {
15  public Vector RightData, LeftData;
16  public Vector VVertexA = Fortune.VVUnkown, VVertexB = Fortune.VVUnkown;
17  public void AddVertex(Vector V)
18  {
19  if(VVertexA==Fortune.VVUnkown)
20  VVertexA = V;
21  else if(VVertexB==Fortune.VVUnkown)
22  VVertexB = V;
23  else throw new Exception("Tried to add third vertex!");
24  }
25  }
26 
27  // VoronoiVertex or VoronoiDataPoint are represented as Vector
28 
29  internal abstract class VNode
30  {
31  private VNode _Parent = null;
32  private VNode _Left = null, _Right = null;
33  public VNode Left
34  {
35  get{return _Left;}
36  set
37  {
38  _Left = value;
39  value.Parent = this;
40  }
41  }
42  public VNode Right
43  {
44  get{return _Right;}
45  set
46  {
47  _Right = value;
48  value.Parent = this;
49  }
50  }
51  public VNode Parent
52  {
53  get{return _Parent;}
54  set{_Parent = value;}
55  }
56 
57 
58  public void Replace(VNode ChildOld, VNode ChildNew)
59  {
60  if(Left==ChildOld)
61  Left = ChildNew;
62  else if(Right==ChildOld)
63  Right = ChildNew;
64  else throw new Exception("Child not found!");
65  ChildOld.Parent = null;
66  }
67 
68  public static VDataNode FirstDataNode(VNode Root)
69  {
70  VNode C = Root;
71  while(C.Left!=null)
72  C = C.Left;
73  return (VDataNode)C;
74  }
75  public static VDataNode LeftDataNode(VDataNode Current)
76  {
77  VNode C = Current;
78  //1. Up
79  do
80  {
81  if(C.Parent==null)
82  return null;
83  if(C.Parent.Left == C)
84  {
85  C = C.Parent;
86  continue;
87  }
88  else
89  {
90  C = C.Parent;
91  break;
92  }
93  }while(true);
94  //2. One Left
95  C = C.Left;
96  //3. Down
97  while(C.Right!=null)
98  C = C.Right;
99  return (VDataNode)C; // Cast statt 'as' damit eine Exception kommt
100  }
101  public static VDataNode RightDataNode(VDataNode Current)
102  {
103  VNode C = Current;
104  //1. Up
105  do
106  {
107  if(C.Parent==null)
108  return null;
109  if(C.Parent.Right == C)
110  {
111  C = C.Parent;
112  continue;
113  }
114  else
115  {
116  C = C.Parent;
117  break;
118  }
119  }while(true);
120  //2. One Right
121  C = C.Right;
122  //3. Down
123  while(C.Left!=null)
124  C = C.Left;
125  return (VDataNode)C; // Cast statt 'as' damit eine Exception kommt
126  }
127 
128  public static VEdgeNode EdgeToRightDataNode(VDataNode Current)
129  {
130  VNode C = Current;
131  //1. Up
132  do
133  {
134  if(C.Parent==null)
135  throw new Exception("No Left Leaf found!");
136  if(C.Parent.Right == C)
137  {
138  C = C.Parent;
139  continue;
140  }
141  else
142  {
143  C = C.Parent;
144  break;
145  }
146  }while(true);
147  return (VEdgeNode)C;
148  }
149 
150  public static VDataNode FindDataNode(VNode Root, double ys, double x)
151  {
152  VNode C = Root;
153  do
154  {
155  if(C is VDataNode)
156  return (VDataNode)C;
157  if(((VEdgeNode)C).Cut(ys,x)<0)
158  C = C.Left;
159  else
160  C = C.Right;
161  }while(true);
162  }
163 
167  public static VNode ProcessDataEvent(VDataEvent e, VNode Root, VoronoiGraph VG, double ys, out VDataNode[] CircleCheckList)
168  {
169  if(Root==null)
170  {
171  Root = new VDataNode(e.DataPoint);
172  CircleCheckList = new VDataNode[] {(VDataNode)Root};
173  return Root;
174  }
175  //1. Find the node to be replaced
176  VNode C = VNode.FindDataNode(Root, ys, e.DataPoint[0]);
177  //2. Create the subtree (ONE Edge, but two VEdgeNodes)
178  VoronoiEdge VE = new VoronoiEdge();
179  VE.LeftData = ((VDataNode)C).DataPoint;
180  VE.RightData = e.DataPoint;
183  VG.Edges.Add(VE);
184 
185  VNode SubRoot;
186  if(Math.Abs(VE.LeftData[1]-VE.RightData[1])<1e-10)
187  {
188  if(VE.LeftData[0]<VE.RightData[0])
189  {
190  SubRoot = new VEdgeNode(VE,false);
191  SubRoot.Left = new VDataNode(VE.LeftData);
192  SubRoot.Right = new VDataNode(VE.RightData);
193  }
194  else
195  {
196  SubRoot = new VEdgeNode(VE,true);
197  SubRoot.Left = new VDataNode(VE.RightData);
198  SubRoot.Right = new VDataNode(VE.LeftData);
199  }
200  CircleCheckList = new VDataNode[] {(VDataNode)SubRoot.Left,(VDataNode)SubRoot.Right};
201  }
202  else
203  {
204  SubRoot = new VEdgeNode(VE,false);
205  SubRoot.Left = new VDataNode(VE.LeftData);
206  SubRoot.Right = new VEdgeNode(VE,true);
207  SubRoot.Right.Left = new VDataNode(VE.RightData);
208  SubRoot.Right.Right = new VDataNode(VE.LeftData);
209  CircleCheckList = new VDataNode[] {(VDataNode)SubRoot.Left,(VDataNode)SubRoot.Right.Left,(VDataNode)SubRoot.Right.Right};
210  }
211 
212  //3. Apply subtree
213  if(C.Parent == null)
214  return SubRoot;
215  C.Parent.Replace(C,SubRoot);
216  return Root;
217  }
218  public static VNode ProcessCircleEvent(VCircleEvent e, VNode Root, VoronoiGraph VG, double ys, out VDataNode[] CircleCheckList)
219  {
220  VDataNode a,b,c;
221  VEdgeNode eu,eo;
222  b = e.NodeN;
223  a = VNode.LeftDataNode(b);
224  c = VNode.RightDataNode(b);
225  if(a==null || b.Parent==null || c==null || !a.DataPoint.Equals(e.NodeL.DataPoint) || !c.DataPoint.Equals(e.NodeR.DataPoint))
226  {
227  CircleCheckList = new VDataNode[]{};
228  return Root; // Abbruch da sich der Graph verändert hat
229  }
230  eu = (VEdgeNode)b.Parent;
231  CircleCheckList = new VDataNode[] {a,c};
232  //1. Create the new Vertex
233  Vector VNew = new Vector(e.Center[0],e.Center[1]);
234 // VNew[0] = Fortune.ParabolicCut(a.DataPoint[0],a.DataPoint[1],c.DataPoint[0],c.DataPoint[1],ys);
235 // VNew[1] = (ys + a.DataPoint[1])/2 - 1/(2*(ys-a.DataPoint[1]))*(VNew[0]-a.DataPoint[0])*(VNew[0]-a.DataPoint[0]);
236  VG.Vertizes.Add(VNew);
237  //2. Find out if a or c are in a distand part of the tree (the other is then b's sibling) and assign the new vertex
238  if(eu.Left==b) // c is sibling
239  {
240  eo = VNode.EdgeToRightDataNode(a);
241 
242  // replace eu by eu's Right
243  eu.Parent.Replace(eu,eu.Right);
244  }
245  else // a is sibling
246  {
247  eo = VNode.EdgeToRightDataNode(b);
248 
249  // replace eu by eu's Left
250  eu.Parent.Replace(eu,eu.Left);
251  }
252  eu.Edge.AddVertex(VNew);
253 // ///////////////////// uncertain
254 // if(eo==eu)
255 // return Root;
256 // /////////////////////
257  eo.Edge.AddVertex(VNew);
258  //2. Replace eo by new Edge
259  VoronoiEdge VE = new VoronoiEdge();
260  VE.LeftData = a.DataPoint;
261  VE.RightData = c.DataPoint;
262  VE.AddVertex(VNew);
263  VG.Edges.Add(VE);
264 
265  VEdgeNode VEN = new VEdgeNode(VE, false);
266  VEN.Left = eo.Left;
267  VEN.Right = eo.Right;
268  if(eo.Parent == null)
269  return VEN;
270  eo.Parent.Replace(eo,VEN);
271  return Root;
272  }
273  public static VCircleEvent CircleCheckDataNode(VDataNode n, double ys)
274  {
277  if(l==null || r==null || l.DataPoint==r.DataPoint || l.DataPoint==n.DataPoint || n.DataPoint==r.DataPoint)
278  return null;
279  if(MathTools.ccw(l.DataPoint[0],l.DataPoint[1],n.DataPoint[0],n.DataPoint[1],r.DataPoint[0],r.DataPoint[1],false)<=0)
280  return null;
282  VCircleEvent VC = new VCircleEvent();
283  VC.NodeN = n;
284  VC.NodeL = l;
285  VC.NodeR = r;
286  VC.Center = Center;
287  VC.Valid = true;
288  if(VC.Y>=ys)
289  return VC;
290  return null;
291  }
292  }
293 
294  internal class VDataNode : VNode
295  {
296  public VDataNode(Vector DP)
297  {
298  this.DataPoint = DP;
299  }
301  }
302 
303  internal class VEdgeNode : VNode
304  {
305  public VEdgeNode(VoronoiEdge E, bool Flipped)
306  {
307  this.Edge = E;
308  this.Flipped = Flipped;
309  }
311  public bool Flipped;
312  public double Cut(double ys, double x)
313  {
314  if(!Flipped)
315  return Math.Round(x-Fortune.ParabolicCut(Edge.LeftData[0], Edge.LeftData[1], Edge.RightData[0], Edge.RightData[1], ys),10);
316  return Math.Round(x-Fortune.ParabolicCut(Edge.RightData[0], Edge.RightData[1], Edge.LeftData[0], Edge.LeftData[1], ys),10);
317  }
318  }
319 
320 
321  internal abstract class VEvent : IComparable
322  {
323  public abstract double Y {get;}
324  public abstract double X {get;}
325  #region IComparable Members
326 
327  public int CompareTo(object obj)
328  {
329  if(!(obj is VEvent))
330  throw new ArgumentException("obj not VEvent!");
331  int i = Y.CompareTo(((VEvent)obj).Y);
332  if(i!=0)
333  return i;
334  return X.CompareTo(((VEvent)obj).X);
335  }
336 
337  #endregion
338  }
339 
340  internal class VDataEvent : VEvent
341  {
343  public VDataEvent(Vector DP)
344  {
345  this.DataPoint = DP;
346  }
347  public override double Y
348  {
349  get
350  {
351  return DataPoint[1];
352  }
353  }
354 
355  public override double X
356  {
357  get
358  {
359  return DataPoint[0];
360  }
361  }
362 
363  }
364 
365  internal class VCircleEvent : VEvent
366  {
367  public VDataNode NodeN, NodeL, NodeR;
368  public Vector Center;
369  public override double Y
370  {
371  get
372  {
373  return Math.Round(Center[1]+MathTools.Dist(NodeN.DataPoint[0],NodeN.DataPoint[1],Center[0],Center[1]),10);
374  }
375  }
376 
377  public override double X
378  {
379  get
380  {
381  return Center[0];
382  }
383  }
384 
385  public bool Valid = true;
386  }
387 
388  public abstract class Fortune
389  {
390  public static readonly Vector VVInfinite = new Vector(double.PositiveInfinity, double.PositiveInfinity);
391  public static readonly Vector VVUnkown = new Vector(double.NaN, double.NaN);
392  internal static double ParabolicCut(double x1, double y1, double x2, double y2, double ys)
393  {
394 // y1=-y1;
395 // y2=-y2;
396 // ys=-ys;
397 //
398 // if(Math.Abs(x1-x2)<1e-10 && Math.Abs(y1-y2)<1e-10)
399 // {
405 // Debug.Log(x1+" "+x2+" "+y1+" "+y2);
406 // throw new Exception("Identical datapoints are not allowed!");
407 // }
408 
409  if(Math.Abs(y1-ys)<1e-10 && Math.Abs(y2-ys)<1e-10)
410  return (x1+x2)/2;
411  if(Math.Abs(y1-ys)<1e-10)
412  return x1;
413  if(Math.Abs(y2-ys)<1e-10)
414  return x2;
415  double a1 = 1/(2*(y1-ys));
416  double a2 = 1/(2*(y2-ys));
417  if(Math.Abs(a1-a2)<1e-10)
418  return (x1+x2)/2;
419  double xs1 = 0.5/(2*a1-2*a2)*(4*a1*x1-4*a2*x2+2*Math.Sqrt(-8*a1*x1*a2*x2-2*a1*y1+2*a1*y2+4*a1*a2*x2*x2+2*a2*y1+4*a2*a1*x1*x1-2*a2*y2));
420  double xs2 = 0.5/(2*a1-2*a2)*(4*a1*x1-4*a2*x2-2*Math.Sqrt(-8*a1*x1*a2*x2-2*a1*y1+2*a1*y2+4*a1*a2*x2*x2+2*a2*y1+4*a2*a1*x1*x1-2*a2*y2));
421  xs1=Math.Round(xs1,10);
422  xs2=Math.Round(xs2,10);
423  if(xs1>xs2)
424  {
425  double h = xs1;
426  xs1=xs2;
427  xs2=h;
428  }
429  if(y1>=y2)
430  return xs2;
431  return xs1;
432  }
433  internal static Vector CircumCircleCenter(Vector A, Vector B, Vector C)
434  {
435  if(A==B || B==C || A==C)
436  throw new Exception("Need three different points!");
437  double tx = (A[0] + C[0])/2;
438  double ty = (A[1] + C[1])/2;
439 
440  double vx = (B[0] + C[0])/2;
441  double vy = (B[1] + C[1])/2;
442 
443  double ux,uy,wx,wy;
444 
445  if(A[0] == C[0])
446  {
447  ux = 1;
448  uy = 0;
449  }
450  else
451  {
452  ux = (C[1] - A[1])/(A[0] - C[0]);
453  uy = 1;
454  }
455 
456  if(B[0] == C[0])
457  {
458  wx = -1;
459  wy = 0;
460  }
461  else
462  {
463  wx = (B[1] - C[1])/(B[0] - C[0]);
464  wy = -1;
465  }
466 
467  double alpha = (wy*(vx-tx)-wx*(vy - ty))/(ux*wy-wx*uy);
468 
469  return new Vector(tx+alpha*ux,ty+alpha*uy);
470  }
471  public static VoronoiGraph ComputeVoronoiGraph(IEnumerable Datapoints)
472  {
474  Hashtable CurrentCircles = new Hashtable();
475  VoronoiGraph VG = new VoronoiGraph();
476  VNode RootNode = null;
477  foreach(Vector V in Datapoints)
478  {
479  PQ.Push(new VDataEvent(V));
480  }
481  while(PQ.Count>0)
482  {
483  VEvent VE = PQ.Pop() as VEvent;
484  VDataNode[] CircleCheckList;
485  if(VE is VDataEvent)
486  {
487  RootNode = VNode.ProcessDataEvent(VE as VDataEvent,RootNode,VG,VE.Y,out CircleCheckList);
488  }
489  else if(VE is VCircleEvent)
490  {
491  CurrentCircles.Remove(((VCircleEvent)VE).NodeN);
492  if(!((VCircleEvent)VE).Valid)
493  continue;
494  RootNode = VNode.ProcessCircleEvent(VE as VCircleEvent,RootNode,VG,VE.Y,out CircleCheckList);
495  }
496  else throw new Exception("Got event of type "+VE.GetType().ToString()+"!");
497  foreach(VDataNode VD in CircleCheckList)
498  {
499  if(CurrentCircles.ContainsKey(VD))
500  {
501  ((VCircleEvent)CurrentCircles[VD]).Valid=false;
502  CurrentCircles.Remove(VD);
503  }
504  VCircleEvent VCE = VNode.CircleCheckDataNode(VD,VE.Y);
505  if(VCE!=null)
506  {
507  PQ.Push(VCE);
508  CurrentCircles[VD]=VCE;
509  }
510  }
511  if(VE is VDataEvent)
512  {
513  Vector DP = ((VDataEvent)VE).DataPoint;
514  foreach(VCircleEvent VCE in CurrentCircles.Values)
515  {
516  if(MathTools.Dist(DP[0],DP[1],VCE.Center[0],VCE.Center[1])<VCE.Y-VCE.Center[1] && Math.Abs(MathTools.Dist(DP[0],DP[1],VCE.Center[0],VCE.Center[1])-(VCE.Y-VCE.Center[1]))>1e-10)
517  VCE.Valid = false;
518  }
519  }
520  }
521  return VG;
522  }
523  }
524 }
static VDataNode LeftDataNode(VDataNode Current)
static VoronoiGraph ComputeVoronoiGraph(IEnumerable Datapoints)
static VDataNode FindDataNode(VNode Root, double ys, double x)
A vector class, implementing all interesting features of vectors
Definition: Vector.cs:10
static Vector CircumCircleCenter(Vector A, Vector B, Vector C)
override bool Equals(object obj)
Compares this vector with another one
Definition: Vector.cs:199
static int ccw(Point P0, Point P1, Point P2, bool PlusOneOnZeroDegrees)
Definition: ToolBox.cs:368
static VNode ProcessCircleEvent(VCircleEvent e, VNode Root, VoronoiGraph VG, double ys, out VDataNode[] CircleCheckList)
static VDataNode FirstDataNode(VNode Root)
double Cut(double ys, double x)
static VCircleEvent CircleCheckDataNode(VDataNode n, double ys)
static double ParabolicCut(double x1, double y1, double x2, double y2, double ys)
void Replace(VNode ChildOld, VNode ChildNew)
Summary description for Hashset.
Definition: HashSet.cs:9
static readonly Vector VVUnkown
static double Dist(double x1, double y1, double x2, double y2)
Definition: ToolBox.cs:33
VEdgeNode(VoronoiEdge E, bool Flipped)
static VDataNode RightDataNode(VDataNode Current)
void Add(object O)
Definition: HashSet.cs:14
static VEdgeNode EdgeToRightDataNode(VDataNode Current)
static VNode ProcessDataEvent(VDataEvent e, VNode Root, VoronoiGraph VG, double ys, out VDataNode[] CircleCheckList)
Will return the new root (unchanged except in start-up)