PolyLink
A library to allow manipulation of geometry from within Mathematica
 All Classes Namespaces Files Functions Variables Properties
Facet.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Wolfram.NETLink;
5 
6 namespace PolyLink
7 {
12  public class Facet : MathLinked
13  {
14  private static readonly Expr _p110 = MSingle.Eval("{1,1,0}");
15  private static readonly PointRef _ref110 = new PointRef(_p110);
16 
20  public string Tag { get; set; }
21 
27  public Facet(HalfEdge head, string color = "RGBColor[1,1,1]")
28  {
29  Head = head;
30  Expr c = ML[color].Eval();
31  if (c.Head != MathConstants.ColorHead)
32  throw new ArgumentException("argument 'color' does not resolve into a Mathematica color");
33  Color = c;
34  }
35 
39  public bool Convex
40  {
41  get
42  {
43  Console.WriteLine("Assuming this facet is convex.");
44  return true;
45  }
46  }
47 
51  public Expr Color { get; set; }
52 
56  public HalfEdge Head { get; set; }
57 
61  public Plane Plane
62  {
63  get { return Head.Plane; }
64  }
65 
70  {
71  foreach (var he in HalfEdges)
72  {
73  he.Facet = this;
74  }
75  }
76 
80  public IEnumerable<HalfEdge> HalfEdges
81  {
82  get { return Head.HEsFromHere; }
83  }
84 
85  public HalfEdge[] HalfEdgeArray
86  {
87  get { return HalfEdges.ToArray(); }
88  }
89 
93  public Expr Graphics
94  {
95  get
96  {
97  return ML["List"].Bracket(
98  ML["List"].Bracket(
99  Color,
100  PolyPrimitive
101  ).Eval()
102  ).Eval();
103  }
104  }
105 
106  private Expr PolyPrimitive
107  {
108  get
109  {
110  return ML["Polygon"].Bracket(
111  ML["List"].Bracket(
112  HalfEdges.Select(he => he.Start.Expr)
113  ).Eval()
114  ).Eval();
115  }
116  }
117 
123  public Expr Highlight
124  {
125  get
126  {
127  return ML["List"].Bracket(
128  ML["List"].Bracket(
129  ML["Glow[Yellow]"].Eval(),
130  PolyPrimitive
131  ).Eval()
132  ).Eval();
133  }
134  }
135 
139  public IEnumerable<PointRef> PointRefs
140  {
141  get { return HalfEdges.Select(he => he.Start); }
142  }
143 
147  public PointRef[] PointRefArray
148  {
149  get { return PointRefs.ToArray(); }
150  }
151 
155  public Expr Points
156  {
157  get { return ML["List"].Bracket(PointRefs.Select(p => p.Expr)).Eval(); }
158  }
159 
160  //Static methods
161 
165  public static Facet TestQuad
166  {
167  get
168  {
169  var a = new HalfEdge(PointRef.OriginRef, PointRef.Ref100);
170  var b = new HalfEdge(a.End, _ref110);
171  var c = new HalfEdge(b.End, PointRef.Ref010);
172  var d = new HalfEdge(c.End, a.Start);
173 
174  a.LinkNext(b);
175  b.LinkNext(c);
176  c.LinkNext(d);
177  d.LinkNext(a);
178 
179  return new Facet(a);
180  }
181  }
182 
187  {
188  get
189  {
190 
191  return new Facet(Head.FacetRingClone, Color.ToString());
192  }
193  }
194 
202  {
203 
204  HalfEdge he1 = HalfEdges.First(he => he.Start == v1);
205  HalfEdge he2 = HalfEdges.First(he => he.Start == v2);
206  HalfEdge he2P = he2.Prev;
207 
208  HalfEdge[] newHes = he1.Prev.DetourToVertex(v2);
209 
210  newHes[0].LinkNext(he2);
211  he2P.LinkNext(newHes[1]);
212  newHes[1].LinkNext(he1);
213 
214  var facet = new Facet(newHes[0]);
215 
216  foreach (HalfEdge halfEdge in newHes[0].HEsFromHere)
217  {
218  halfEdge.Facet = facet;
219  }
220 
221  return new Facet(newHes[0]);
222  }
223 
228  public IEnumerable<Facet> Triangulate()
229  {
230  Console.WriteLine("Triangulating!");
231  return HalfEdges.Skip(3)
232  .Select(he => he.Start).ToList()
233  .Select(vert => SubDivide(vert, Head.End));
234  }
235 
242  {
243  //Console.WriteLine("c1");
244  Expr t = FindIntersectSlow(p);
245  //Console.WriteLine("c2");
246  if (t == null)
247  return false;
248  Expr point = ML["{0}*{1} + (1-{0})*{2}"].Format(t, p.StartRef.Expr, p.EndRef.Expr).Eval();
249  //Console.WriteLine("c3");
250  Expr cp =
251  ML["Cross[{1} - {0},{2} - {0}]"].Format(point, PointRefs.Last().Expr, PointRefs.First().Expr).Eval();
252  //Console.WriteLine("c4");
253  return !PointRefs.CirclularPairs()
254  //.Print(any => "c5")
255  .Select(
256  pair =>
257  ML["Cross[{1} - {0},{2} - {0}]"].Format(point, pair.Item1.Expr, pair.Item2.Expr)
258  .Eval())
259  //.Print(any => "c6")
260  .Any(crossProduct => (bool) ML["Dot[{0},{1}] <= 0"].Format(cp, crossProduct).EvalObject());
261  }
262 
263  private Expr FindIntersectSlow(EndPointPacket p)
264  {
265  //Console.WriteLine("f1");
266  Expr tNum =
267  ML["Dot[{0},{1}] - Dot[{2},{1}]"].Format(p.EndRef.Expr, Plane.Normal, Plane.Point)
268  .FullSimplify()
269  .Eval();
270  //Console.WriteLine("f2");
271  Expr tDenom =
272  ML["Dot[{0}.{1}] - Dot[{1}.{2}]"].Format(p.EndRef.Expr, Plane.Normal, p.StartRef.Expr)
273  .FullSimplify()
274  .Eval();
275  //Console.WriteLine("f3");
276  if ((bool) ML["{0} == 0"].Format(tDenom).FullSimplify().EvalObject())
277  {
278  //Console.WriteLine("f4a");
279  //Either on plane or parallel
280  if ((bool) ML["{0} == 0"].Format(tNum).FullSimplify().EvalObject())
281  {
282  return "(1/2)".MsEvalWith();
283  }
284  return null;
285  }
286  //Console.WriteLine("f4b");
287  Expr q = ML["{0}/{1}"].Format(tNum, tDenom).FullSimplify().Eval();
288  // Console.WriteLine("f5b");
289  return ((bool) ML["{0} > 0 && {0} < 1"].Format(q).FullSimplify().EvalObject())
290  ? q
291  : null;
292  }
293 
299  public bool CheckIntersectSlow(Facet f)
300  {
301  if (PlaneCheck(f))
302  {
303  return false;
304  }
305  return HalfEdges.Any(he => f.CheckIntersectSlow(he.EndPointPacket));
306  }
307 
308  private bool PlaneCheck(Facet f)
309  {
310  Expr normal = Plane.Normal;
311 
312  bool pointsInFront = false;
313  bool pointsInBack = false;
314 
315  Expr headPt = Head.Start.Expr;
316 
317  foreach (Expr vector in f.PointRefs
318  .Select(pr => pr.Expr)
319  .Select(p =>
320  ML["{0} - {1}"].Format(p, headPt).Eval()))
321  {
322  Expr dot = ML["Dot"].Bracket(vector, normal).FullSimplify().Eval();
323  if ((bool) ML["{0} > 0"].Format(dot).EvalObject())
324  {
325  pointsInFront = true;
326  }
327  if ((bool) ML["{0} < 0"].Format(dot).EvalObject())
328  {
329  pointsInBack = true;
330  }
331  if (pointsInBack && pointsInFront)
332  {
333  return false;
334  }
335  }
336  return true;
337  }
338 
342  public Expr Midpoint
343  {
344  get { return ML["Mean"].Bracket(Points).Eval(); }
345  }
346 
352  public Expr InsetLabel(int labelNumber)
353  {
354  var labelpoint = ML["{0} + .1*{1}"].Format(Midpoint, Plane.Normal).Eval();
355  return ML["Inset"].Bracket(labelNumber, labelpoint).Eval();
356  }
357 
363  public Expr ClipLabel(int labelNumber)
364  {
365  return ML["Text"].Bracket(labelNumber, Midpoint).Eval();
366  }
367 
373  public Expr HELabelsStartingFrom(int startFrom)
374  {
375  var labels = Enumerable.Range(startFrom, int.MaxValue).NetZip(HalfEdges, (i, h) => h.Label(i));
376  return
377  ML["List"].Bracket
378  (
379  ML["List"]
380  .Bracket(labels)
381  .Eval()
382  ).Eval();
383  }
384 
388  public Expr HELabels
389  {
390  get { return HELabelsStartingFrom(1); }
391  }
392 
398  public HalfEdge PickHalfEdge(int i)
399  {
400  return HalfEdges.Skip(i - 1).First();
401  }
402 
406  public Expr Normal
407  {
408  get { return Plane.Normal; }
409  }
410 
414  public Expr NormalGraphics
415  {
416  get
417  {
418  return ML["List[ {{Purple, Arrow[ {{ {0}, {0} + .5*{1} }} ] }} ]"].Format(Midpoint, Normal).Eval();
419  }
420  }
421 
425  public Expr HEHighlights
426  {
427  get
428  {
429  return ML["List"].Bracket(
430  ML["List"].Bracket(HalfEdges.Select(he => he.Highlight)).Eval()
431  ).Eval();
432  }
433  }
434  }
435 }