PolyLink
A library to allow manipulation of geometry from within Mathematica
 All Classes Namespaces Files Functions Variables Properties
HalfEdge.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using Wolfram.NETLink;
7 
8 namespace PolyLink
9 {
14  public class HalfEdge : MathLinked
15  {
16 
20  public Facet Facet { get; set; }
21 
25  public string Tag
26  {
27  get { return Facet.Tag; }
28  }
29 
34  {
35  get
36  {
37  return new EndPointPacket(Start, End);
38  }
39  }
40 
45  {
46  get
47  {
49  }
50  }
51 
55  public PointRef Start
56  {
57  get { return _start; }
58  set { _start = value; }
59  }
60 
64  public PointRef End
65  {
66  get { return _end; }
67  set { _end = value; }
68  }
69 
70  protected PointRef _start;
71  protected PointRef _end;
72 
73  protected HalfEdge _opposite;
74 
78  public HalfEdge Opposite
79  {
80  get { return _opposite; }
81  set
82  {
83  _opposite = value;
84  }
85  }
86 
87  protected HalfEdge _next;
88  protected HalfEdge _prev;
89 
93  public HalfEdge Next
94  {
95  get { return _next; }
96  }
97 
101  public HalfEdge Prev
102  {
103  get { return _prev; }
104  }
105 
109  public IEnumerable<HalfEdge> HEsFromHere
110  {
111  get
112  {
113  yield return this;
114  var he = this.Next;
115  while (he != this)
116  {
117  yield return he;
118  he = he.Next;
119  }
120  }
121  }
122 
126  public IEnumerable<HalfEdge> HEsFromHereReversed
127  {
128  get
129  {
130  yield return this;
131  var he = this.Prev;
132  while (he != this)
133  {
134  yield return he;
135  he = he.Prev;
136  }
137  }
138  }
139 
143  public Expr AsVectorExpr
144  {
145  get { return EndPointPacket.AsVector; }
146  }
147 
151  public Vector AsVector
152  {
153  get {return new Vector(this.AsVectorExpr);}
154  }
155 
159  public Plane Plane
160  {
161  get
162  {
163  Expr point = Start.Expr;
164  Expr normal = ML["Normalize[Cross[{0},{1}]]"].Format(this.AsVectorExpr, Next.AsVectorExpr).FullSimplify().Eval();
165  return new Plane(point, normal);
166  }
167  }
168 
172  public Expr DotOpposite
173  {
174  get { return ML["Dot[{0},{1}]"].Format(this.Plane.Normal, Opposite.Plane.Normal).Eval(); }
175  }
176 
182  {
183  var ret = new HalfEdge[2];
184  var outHE = new HalfEdge(End, p)
185  {
186  Facet = Facet
187  };
188  var backHE = new HalfEdge(p, End)
189  {
190  Facet = Facet
191  };
192  outHE.Opposite = backHE;
193  backHE.Opposite = outHE;
194 
195  var oldNext = _next;
196  _next = outHE;
197  outHE._prev = this;
198  outHE._next = backHE;
199  backHE._prev = outHE;
200  backHE._next = oldNext;
201 
202  ret[0] = outHE;
203  ret[1] = backHE;
204  return ret;
205  }
206 
212  public Dictionary<HalfEdge, HalfEdge> LinkNext(HalfEdge he)
213  {
214  var d = new Dictionary<HalfEdge, HalfEdge>();
215  d[this] = Next;
216  d[he] = he.Prev;
217  _next = he;
218  he._prev = this;
219  return d;
220  }
221 
225  public bool OppIsNull
226  {
227  get { return Opposite == null; }
228  }
229 
233  public void MergeAcross()
234  {
235  var prev = Prev;
236  prev.LinkNext(Opposite.Next);
237  Opposite.Prev.LinkNext(Next);
238  Opposite.Facet.Head = null;
239  foreach (var halfEdge in prev.HEsFromHere)
240  {
241  halfEdge.Facet = Facet;
242  }
243  }
244 
245  public override string ToString()
246  {
247  return String.Format("[{0} to {1}]", Start, End);
248  }
249 
254  public bool DontCycle { get; set; }
255 
259  public IEnumerable<HalfEdge> CycleAboutEndPoint
260  {
261  get
262  {
263  if (DontCycle)
264  {
265  yield break;
266  }
267 
268  yield return this;
269  var he = this.Next.Opposite;
270  while (he != this)
271  {
272  yield return he;
273  he = he.Next.Opposite;
274  }
275  }
276  }
277 
281  public Expr DotNext
282  {
283  get { return ML["Dot"].Bracket(this.AsVectorExpr, this.Next.EndPointPacket.Opposite.AsVector).Eval(); }
284  }
285 
289  public Expr AngleToNext
290  {
291  get
292  {
293  return ML["ArcCos[{0}] / ({1} * {2})"].Format(this.DotNext, this.EndPointPacket.Magnitude,
294  this.Next.EndPointPacket.Magnitude).Eval();
295  }
296  }
297 
301  public Expr SumAngles
302  {
303  get
304  {
305  return this.CycleAboutEndPoint.Select(he => he.AngleToNext).Aggregate((a1, a2) => ML["{0} + {1}"].Format(a1,a2).Eval());
306  }
307  }
308 
312  public Expr Curvature
313  {
314  get { return ML["2*Pi - {0}"].Format(SumAngles).Eval(); }
315  }
316 
320  public Expr Highlight
321  {
322  get { return ML["List[ {{ Thick, Red, Arrow[ {{ {0},{1} }} ] }} ]"].Format(Start.Expr, End.Expr).Eval(); }
323  }
324 
325  //Static methods
326 
327  private static readonly HalfEdge littleLine = new HalfEdge(PointRef.OriginRef, PointRef.Ref100);
328  private static readonly HalfEdge line2 = new HalfEdge(PointRef.Ref100, PointRef.Ref010);
329  private static readonly HalfEdge line3 = new HalfEdge(PointRef.Ref010, PointRef.OriginRef);
330 
334  public static HalfEdge LittleLine
335  {
336  get { return littleLine; }
337  }
338 
342  public static HalfEdge Facetable
343  {
344  get
345  {
346  if (LittleLine.Next == null)
347  {
348  LittleLine.LinkNext(line2);
349  line2.LinkNext(line3);
350  line3.LinkNext(LittleLine);
351  }
352  return LittleLine;
353  }
354  }
355 
359  public Expr NormalGraphics
360  {
361  get { return ML["List[ {{Purple, Arrow[ {{ {0}, {0} + {1} }} ] }} ]"].Format(End.Expr, Plane.Normal).Eval(); }
362  }
363  //End section
364 
365  public HalfEdge(PointRef start, PointRef end)
366  {
367  _start = start;
368  _end = end;
369  DontCycle = false;
370  }
371 
375  public bool Torn
376  {
377  get
378  {
379  return (Opposite == null)
380  || (Opposite.Start != End)
381  || (Opposite.End != Start);
382  }
383  }
384 
388  public void Unfold(IEnumerable<PointRef> stowaways = null, IEnumerable<HalfEdge> stowawayHalfEdges = null)
389  {
390 
391  if (stowaways == null)
392  {
393  stowaways = Enumerable.Empty<PointRef>();
394  }
395  if (stowawayHalfEdges == null)
396  {
397  stowawayHalfEdges = Enumerable.Empty<HalfEdge>();
398  }
399 
400  var transVect = Vector.FromPoints(this.Start.Expr, Opposite.End.Expr);
401  var translation = ML["TranslationTransform"].Bracket(transVect.Expr).Eval();
402  TransformFacet(translation);
403  foreach (var stowaway in stowaways)
404  {
405  stowaway.Transform(translation);
406  }
407  foreach (var halfedge in stowawayHalfEdges)
408  {
409  halfedge.Transform(translation,
410  includeStart: !halfedge.Start.EqualsAny(Start,End),
411  includeEnd: !halfedge.End.EqualsAny(Start,End));
412  }
413 
414  var rotation1 = ML["RotationTransform"].Bracket(
415  ML["List"].Bracket(
416  this.Plane.Normal,
417  Opposite.Plane.Normal
418  ).Eval(),
419  Opposite.End.Expr
420  ).Eval();
421  TransformFacet(rotation1);
422  foreach (var stowaway in stowaways)
423  {
424  stowaway.Transform(rotation1);
425  }
426  foreach (var halfedge in stowawayHalfEdges)
427  {
428  halfedge.Transform(rotation1,
429  includeStart: !halfedge.Start.EqualsAny(Start, End),
430  includeEnd: !halfedge.End.EqualsAny(Start, End));
431  }
432 
433  var rotation2 = ML["RotationTransform"].Bracket(
434  ML["List"].Bracket(
435  this.AsVectorExpr,
436  ML["-1 * {0}"].Format(Opposite.AsVectorExpr).Eval()
437  ).Eval(),
438  Opposite.End.Expr
439  ).Eval();
440  TransformFacet(rotation2);
441  foreach (var stowaway in stowaways)
442  {
443  stowaway.Transform(rotation2);
444  }
445  foreach (var halfedge in stowawayHalfEdges)
446  {
447  halfedge.Transform(rotation2,
448  includeStart: !halfedge.Start.EqualsAny(Start, End),
449  includeEnd: !halfedge.End.EqualsAny(Start, End));
450  }
451 
452  //This 'snapping' code is part of correct behavior, but we sometimes
453  //comment it out to check our transforms.
454 
455  Start = Opposite.End;
456  End = Opposite.Start;
457  Prev.End = Start;
458  Next.Start = End;
459  }
460 
465  public void TransformFacet(Expr matrix)
466  {
467  //For every HalfEdge other than myself
468  foreach (var halfEdge in HEsFromHere)
469  {
470  halfEdge.Transform(matrix);
471  }
472 
473  foreach (var halfEdge in HEsFromHere)
474  {
475  halfEdge.Next.Start = halfEdge.End;
476  }
477  }
478 
482  public void UselessTransform()
483  {
484  var m = ML["TranslationTransform"].Bracket("{0,0,0}").Eval();
485  TransformFacet(m);
486  }
487 
492  public void Anchor(IEnumerable<PointRef> stowaways)
493  {
494  if (stowaways == null)
495  {
496  stowaways = Enumerable.Empty<PointRef>();
497  }
498 
499  var transVec = new EndPointPacket(this.Start, PointRef.OriginRef).AsVector;
500  var translation = ML["TranslationTransform"].Bracket(transVec).Eval();
501  TransformFacet(translation);
502  foreach (var stowaway in stowaways)
503  {
504  stowaway.Transform(translation);
505  }
506 
507 
508  var rotation1 = ML["RotationTransform"].Bracket(
509  ML["List"].Bracket(
510  this.Plane.Normal,
511  ML["{0,0,1}"].Eval()
512  ).Eval(),
514  ).Eval();
515  TransformFacet(rotation1);
516  foreach (var stowaway in stowaways)
517  {
518  stowaway.Transform(rotation1);
519  }
520 
521  var rotation2 = ML["RotationTransform"].Bracket(
522  ML["List"].Bracket(
523  this.AsVectorExpr,
524  ML["{1,0,0}"].Eval()
525  ).Eval()
526  ).Eval();
527  TransformFacet(rotation2);
528  foreach (var stowaway in stowaways)
529  {
530  stowaway.Transform(rotation2);
531  }
532 
533  var rotation3 = ML["RotationTransform"].Bracket(
534  ML["List"].Bracket(
535  this.Plane.Normal,
536  ML["{0,0,1}"].Eval()
537  ).Eval(),
539  ).Eval();
540  TransformFacet(rotation3);
541  foreach (var stowaway in stowaways)
542  {
543  stowaway.Transform(rotation3);
544  }
545  }
550  public void Transform(Expr transform, bool includeStart = true, bool includeEnd = true)
551  {
552 
553  if (includeStart)
554  {
555  Start = new PointRef(
556  ML["{0}[{1}]"].Format(transform, Start.Expr).Eval());
557  }
558  if (includeEnd)
559  {
560  End = new PointRef(
561  ML["{0}[{1}]"].Format(transform, End.Expr).Eval());
562  }
563  }
564 
568  public Expr Graphics
569  {
570  get
571  {
572  if (Torn)
573  {
574  return ML["List[{{ Dashed, Arrow[ {{ {0}, {1} }}] }}]"].Format(Start.Expr, Opposite.End.Expr).Eval();
575  }
576  return null;
577  }
578  }
579 
584  public void NegativeTranslate(Vector v)
585  {
586  var translation = ML["TranslationTransform[-{0}]"].Format(v.Expr).Eval();
587  TransformFacet(translation);
588  }
589 
594  {
595  get {
596  var clone = FacetRingClone;
597  var clonePairs = HEsFromHere
598  .NetZip(clone.HEsFromHere,
599  (o, c) => new WorkingCopy<HalfEdge>(o,c));
600  foreach (var cp in clonePairs)
601  {
602  cp.Copy.Facet = cp.Original.Facet;
603  cp.Copy.Opposite = cp.Original.Opposite;
604  }
605  return clone;
606  }
607  }
608 
612  public HalfEdge FacetRingClone
613  {
614  get
615  {
616  var cloneRing = HEsFromHere
617  .Select(he => he.ShallowClone).Skip(1);
618  var prev = ShallowClone;
619  var myClone = prev;
620 
621  foreach (var he in cloneRing.Concat(new[] { myClone }))
622  {
623  he.Start = he.Start.DeepClone;
624  prev.End = he.Start;
625  prev.LinkNext(he);
626  prev = he;
627  }
628 
629  return myClone;
630  }
631  }
632 
636  protected HalfEdge ShallowClone
637  {
638  get
639  {
640  var r= new HalfEdge(Start, End)
641  {
642  _next = Next,
643  _prev = Prev,
644  _opposite = null,
645  };
646  r.DontCycle = true;
647  return r;
648  }
649  }
650 
654  public Expr Midpoint
655  {
656  get { return ML["Mean"].Bracket(
657  ML["List"].Bracket(Start.Expr, End.Expr).Eval()).Eval(); }
658  }
659 
665  public Expr Label(int i)
666  {
667  var labelPoint = ML["{0} + .05*{1}"].Format(Midpoint, Plane.Normal).Eval();
668  return ML["Inset"].Bracket(i, labelPoint, "BaseStyle -> {Darker[Blue], Italic}").Eval();
669  }
670 
671 
672  public void MoveStartPoint(Expr p)
673  {
674  Start.Expr = p;
675  }
676 
677  public Facet ForceNewFacet
678  {
679  get
680  {
681  return new Facet(this);
682  }
683  }
684 
690  public IEnumerable<HalfEdge> BreadthFirstGrab(bool includeTorn = false)
691  {
692  var dontGrab = new HashSet<HalfEdge>();
693  var queue = new Queue<HalfEdge>();
694  var grab = new List<HalfEdge>();
695 
696  queue.Enqueue(this);
697  dontGrab.UnionWith(this.Opposite.HEsFromHere.ToHashSet());
698 
699  while (queue.Count > 0)
700  {
701  var he = queue.Dequeue();
702  if (dontGrab.Contains(he))
703  continue;
704 
705  grab.Add(he);
706  dontGrab.UnionWith(he.HEsFromHere.ToHashSet());
707 
708  var opposites = he.HEsFromHere.Skip(1).Select(h => h.Opposite).ToList();
709  var validOpps = opposites
710  .Where(h => !dontGrab.Contains(h))
711  .Where(h => includeTorn || !h.Torn).ToList();
712  foreach (var halfedge in validOpps)
713  {
714  queue.Enqueue(halfedge);
715  }
716  }
717 
718  return grab;
719  }
720 
721  public IEnumerable<PointRef> StartPointsFromHere
722  {
723  get { return HEsFromHere.Select(he => he.Start); }
724  }
725 
731  {
732  var facetHesTorn = HEsFromHere.Select(he => he.Torn).ToList();
733 
734  var hes = BreadthFirstGrabAll();
735  Unfold(null, hes);
736  p.FusePoints();
737  //Prev.End = Start;
738  //Next.Start = End;
739 
740  var facetHes_facetHesTorn = HEsFromHere.NetZip(facetHesTorn, (he, b) => new Tuple<HalfEdge,bool>(he,b));
741  foreach (var tuple in facetHes_facetHesTorn)
742  {
743  HalfEdge h = tuple.Item1;
744  bool wasTorn = tuple.Item2;
745 
746  if (!wasTorn)
747  {
748  h.Start = h.Opposite.End;
749  h.End = h.Opposite.Start;
750  }
751  }
752  }
753 
754  public IEnumerable<PointRef> BreadthFirstPoints()
755  {
756  return BreadthFirstGrab().Except(new[] {this}).SelectMany(he => he.StartPointsFromHere).Except(new[] {Start,End}).ToHashSet();
757  }
758 
759  public IEnumerable<HalfEdge> BreadthFirstGrabAll()
760  {
761  return BreadthFirstGrab().Except(new[] {this}).SelectMany(he => he.HEsFromHere).ToHashSet();
762  }
763  }
764 }