Solvespace
920 строк · 27.2 Кб
1//-----------------------------------------------------------------------------
2// Operations on polygons (planar, of line segment edges).
3//
4// Copyright 2008-2013 Jonathan Westhues.
5//-----------------------------------------------------------------------------
6#include "solvespace.h"
7
8Vector STriangle::Normal() const {
9Vector ab = b.Minus(a), bc = c.Minus(b);
10return ab.Cross(bc);
11}
12
13double STriangle::MinAltitude() const {
14double altA = a.DistanceToLine(b, c.Minus(b)),
15altB = b.DistanceToLine(c, a.Minus(c)),
16altC = c.DistanceToLine(a, b.Minus(a));
17
18return min(altA, min(altB, altC));
19}
20
21bool STriangle::ContainsPoint(Vector p) const {
22Vector n = Normal();
23if(MinAltitude() < LENGTH_EPS) {
24// shouldn't happen; zero-area triangle
25return false;
26}
27return ContainsPointProjd(n.WithMagnitude(1), p);
28}
29
30bool STriangle::ContainsPointProjd(Vector n, Vector p) const {
31Vector ab = b.Minus(a), bc = c.Minus(b), ca = a.Minus(c);
32
33Vector no_ab = n.Cross(ab);
34if(no_ab.Dot(p) < no_ab.Dot(a) - LENGTH_EPS) return false;
35
36Vector no_bc = n.Cross(bc);
37if(no_bc.Dot(p) < no_bc.Dot(b) - LENGTH_EPS) return false;
38
39Vector no_ca = n.Cross(ca);
40if(no_ca.Dot(p) < no_ca.Dot(c) - LENGTH_EPS) return false;
41
42return true;
43}
44
45bool STriangle::Raytrace(const Vector &rayPoint, const Vector &rayDir,
46double *t, Vector *inters) const {
47// Algorithm from: "Fast, Minimum Storage Ray/Triangle Intersection" by
48// Tomas Moeller and Ben Trumbore.
49
50// Find vectors for two edges sharing vertex A.
51Vector edge1 = b.Minus(a);
52Vector edge2 = c.Minus(a);
53
54// Begin calculating determinant - also used to calculate U parameter.
55Vector pvec = rayDir.Cross(edge2);
56
57// If determinant is near zero, ray lies in plane of triangle.
58// Also, cull back facing triangles here.
59double det = edge1.Dot(pvec);
60if(-det < LENGTH_EPS) return false;
61double inv_det = 1.0f / det;
62
63// Calculate distance from vertex A to ray origin.
64Vector tvec = rayPoint.Minus(a);
65
66// Calculate U parameter and test bounds.
67double u = tvec.Dot(pvec) * inv_det;
68if (u < 0.0f || u > 1.0f) return false;
69
70// Prepare to test V parameter.
71Vector qvec = tvec.Cross(edge1);
72
73// Calculate V parameter and test bounds.
74double v = rayDir.Dot(qvec) * inv_det;
75if (v < 0.0f || u + v > 1.0f) return false;
76
77// Calculate t, ray intersects triangle.
78*t = edge2.Dot(qvec) * inv_det;
79
80// Calculate intersection point.
81if(inters != NULL) *inters = rayPoint.Plus(rayDir.ScaledBy(*t));
82
83return true;
84}
85
86double STriangle::SignedVolume() const {
87return a.Dot(b.Cross(c)) / 6.0;
88}
89
90double STriangle::Area() const {
91Vector ab = a.Minus(b);
92Vector cb = c.Minus(b);
93return ab.Cross(cb).Magnitude() / 2.0;
94}
95
96bool STriangle::IsDegenerate() const {
97return a.OnLineSegment(b, c) ||
98b.OnLineSegment(a, c) ||
99c.OnLineSegment(a, b);
100}
101
102void STriangle::FlipNormal() {
103swap(a, b);
104swap(an, bn);
105}
106
107STriangle STriangle::Transform(Vector u, Vector v, Vector n) const {
108STriangle tr = *this;
109tr.a = tr.a.ScaleOutOfCsys(u, v, n);
110tr.an = tr.an.ScaleOutOfCsys(u, v, n);
111tr.b = tr.b.ScaleOutOfCsys(u, v, n);
112tr.bn = tr.bn.ScaleOutOfCsys(u, v, n);
113tr.c = tr.c.ScaleOutOfCsys(u, v, n);
114tr.cn = tr.cn.ScaleOutOfCsys(u, v, n);
115return tr;
116}
117
118STriangle STriangle::From(STriMeta meta, Vector a, Vector b, Vector c) {
119STriangle tr = {};
120tr.meta = meta;
121tr.a = a;
122tr.b = b;
123tr.c = c;
124return tr;
125}
126
127SEdge SEdge::From(Vector a, Vector b) {
128SEdge se = {};
129se.a = a;
130se.b = b;
131return se;
132}
133
134bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) const {
135Vector d = eb.Minus(ea);
136double t_eps = LENGTH_EPS/d.Magnitude();
137
138double t, tthis;
139bool skew;
140Vector pi;
141bool inOrEdge0, inOrEdge1;
142
143Vector dthis = b.Minus(a);
144double tthis_eps = LENGTH_EPS/dthis.Magnitude();
145
146if((ea.Equals(a) && eb.Equals(b)) ||
147(eb.Equals(a) && ea.Equals(b)))
148{
149if(ppi) *ppi = a;
150if(spl) spl->Add(a);
151return true;
152}
153
154// Can't just test if distance between d and a equals distance between d and b;
155// they could be on opposite sides, since we don't have the sign.
156double m = sqrt(d.Magnitude()*dthis.Magnitude());
157if(sqrt(fabs(d.Dot(dthis))) > (m - LENGTH_EPS)) {
158// The edges are parallel.
159if(fabs(a.DistanceToLine(ea, d)) > LENGTH_EPS) {
160// and not coincident, so can't be intersecting
161return false;
162}
163// The edges are coincident. Make sure that neither endpoint lies
164// on the other
165bool inters = false;
166double t;
167t = a.Minus(ea).DivProjected(d);
168if(t > t_eps && t < (1 - t_eps)) inters = true;
169t = b.Minus(ea).DivProjected(d);
170if(t > t_eps && t < (1 - t_eps)) inters = true;
171t = ea.Minus(a).DivProjected(dthis);
172if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
173t = eb.Minus(a).DivProjected(dthis);
174if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
175
176if(inters) {
177if(ppi) *ppi = a;
178if(spl) spl->Add(a);
179return true;
180} else {
181// So coincident but disjoint, okay.
182return false;
183}
184}
185
186// Lines are not parallel, so look for an intersection.
187pi = Vector::AtIntersectionOfLines(ea, eb, a, b,
188&skew,
189&t, &tthis);
190if(skew) return false;
191
192inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
193inOrEdge1 = (tthis > -tthis_eps) && (tthis < (1 + tthis_eps));
194
195if(inOrEdge0 && inOrEdge1) {
196if(a.Equals(ea) || b.Equals(ea) ||
197a.Equals(eb) || b.Equals(eb))
198{
199// Not an intersection if we share an endpoint with an edge
200return false;
201}
202// But it's an intersection if a vertex of one edge lies on the
203// inside of the other (or if they cross away from either's
204// vertex).
205if(ppi) *ppi = pi;
206if(spl) spl->Add(pi);
207return true;
208}
209return false;
210}
211
212void SEdgeList::Clear() {
213l.Clear();
214}
215
216void SEdgeList::AddEdge(Vector a, Vector b, int auxA, int auxB, int tag) {
217SEdge e = {};
218e.tag = tag;
219e.a = a;
220e.b = b;
221e.auxA = auxA;
222e.auxB = auxB;
223l.Add(&e);
224}
225
226bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest,
227SEdge *errorAt, bool keepDir, int start) const
228{
229int i;
230
231dest->AddPoint(first);
232dest->AddPoint(last);
233
234do {
235for(i = start; i < l.n; i++) {
236/// @todo fix const!
237SEdge *se = const_cast<SEdge*>(&(l[i]));
238if(se->tag) continue;
239
240if(se->a.Equals(last)) {
241dest->AddPoint(se->b);
242last = se->b;
243se->tag = 1;
244break;
245}
246// Don't allow backwards edges if keepDir is true.
247if(!keepDir && se->b.Equals(last)) {
248dest->AddPoint(se->a);
249last = se->a;
250se->tag = 1;
251break;
252}
253}
254if(i >= l.n) {
255// Couldn't assemble a closed contour; mark where.
256if(errorAt) {
257errorAt->a = first;
258errorAt->b = last;
259}
260return false;
261}
262
263} while(!last.Equals(first));
264
265return true;
266}
267
268bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) const {
269dest->Clear();
270
271bool allClosed = true;
272Vector first = Vector::From(0, 0, 0);
273Vector last = Vector::From(0, 0, 0);
274int i;
275for(i = 0; i < l.n; i++) {
276if(!l[i].tag) {
277first = l[i].a;
278last = l[i].b;
279/// @todo fix const!
280const_cast<SEdge*>(&(l[i]))->tag = 1;
281// Create a new empty contour in our polygon, and finish assembling
282// into that contour.
283dest->AddEmptyContour();
284if(!AssembleContour(first, last, dest->l.Last(), errorAt, keepDir, i+1)) {
285allClosed = false;
286}
287// But continue assembling, even if some of the contours are open
288}
289}
290return allClosed;
291}
292
293//-----------------------------------------------------------------------------
294// Test if the specified edge crosses any of the edges in our list. Two edges
295// are not considered to cross if they share an endpoint (within LENGTH_EPS),
296// but they are considered to cross if they are coincident and overlapping.
297// If pi is not NULL, then a crossing is returned in that.
298//-----------------------------------------------------------------------------
299int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, Vector *ppi, SPointList *spl) const {
300auto cnt = std::count_if(l.begin(), l.end(),
301[&](SEdge const &se) { return se.EdgeCrosses(a, b, ppi, spl); });
302return static_cast<int>(cnt);
303}
304
305//-----------------------------------------------------------------------------
306// Returns true if the intersecting edge list contains an edge that shares
307// an endpoint with one of our edges.
308//-----------------------------------------------------------------------------
309bool SEdgeList::ContainsEdgeFrom(const SEdgeList *sel) const {
310for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
311if(sel->ContainsEdge(se)) return true;
312}
313return false;
314}
315bool SEdgeList::ContainsEdge(const SEdge *set) const {
316for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
317if((se->a).Equals(set->a) && (se->b).Equals(set->b)) return true;
318if((se->b).Equals(set->a) && (se->a).Equals(set->b)) return true;
319}
320return false;
321}
322
323//-----------------------------------------------------------------------------
324// Remove unnecessary edges:
325// - if two are anti-parallel then
326// if both=true, remove both
327// else remove only one.
328// - if two are parallel then remove one.
329//-----------------------------------------------------------------------------
330void SEdgeList::CullExtraneousEdges(bool both) {
331l.ClearTags();
332for(int i = 0; i < l.n; i++) {
333SEdge *se = &(l[i]);
334for(int j = i + 1; j < l.n; j++) {
335SEdge *set = &(l[j]);
336if((set->a).Equals(se->a) && (set->b).Equals(se->b)) {
337// Two parallel edges exist; so keep only the first one.
338set->tag = 1;
339}
340if((set->a).Equals(se->b) && (set->b).Equals(se->a)) {
341// Two anti-parallel edges exist; if both=true, keep neither,
342// otherwise keep only one.
343if (both) se->tag = 1;
344set->tag = 1;
345}
346}
347}
348l.RemoveTagged();
349}
350
351//-----------------------------------------------------------------------------
352// Make a kd-tree of edges. This is used for O(log(n)) implementations of stuff
353// that would naively be O(n).
354//-----------------------------------------------------------------------------
355SKdNodeEdges *SKdNodeEdges::Alloc() {
356SKdNodeEdges *ne = (SKdNodeEdges *)AllocTemporary(sizeof(SKdNodeEdges));
357*ne = {};
358return ne;
359}
360SEdgeLl *SEdgeLl::Alloc() {
361SEdgeLl *sell = (SEdgeLl *)AllocTemporary(sizeof(SEdgeLl));
362*sell = {};
363return sell;
364}
365SKdNodeEdges *SKdNodeEdges::From(SEdgeList *sel) {
366SEdgeLl *sell = NULL;
367SEdge *se;
368for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
369SEdgeLl *n = SEdgeLl::Alloc();
370n->se = se;
371n->next = sell;
372sell = n;
373}
374return SKdNodeEdges::From(sell);
375}
376SKdNodeEdges *SKdNodeEdges::From(SEdgeLl *sell) {
377SKdNodeEdges *n = SKdNodeEdges::Alloc();
378
379// Compute the midpoints (just mean, though median would be better) of
380// each component.
381Vector ptAve = Vector::From(0, 0, 0);
382SEdgeLl *flip;
383int totaln = 0;
384for(flip = sell; flip; flip = flip->next) {
385ptAve = ptAve.Plus(flip->se->a);
386ptAve = ptAve.Plus(flip->se->b);
387totaln++;
388}
389ptAve = ptAve.ScaledBy(1.0 / (2*totaln));
390
391// For each component, see how it splits.
392int ltln[3] = { 0, 0, 0 }, gtln[3] = { 0, 0, 0 };
393double badness[3];
394for(flip = sell; flip; flip = flip->next) {
395for(int i = 0; i < 3; i++) {
396if(flip->se->a.Element(i) < ptAve.Element(i) + KDTREE_EPS ||
397flip->se->b.Element(i) < ptAve.Element(i) + KDTREE_EPS)
398{
399ltln[i]++;
400}
401if(flip->se->a.Element(i) > ptAve.Element(i) - KDTREE_EPS ||
402flip->se->b.Element(i) > ptAve.Element(i) - KDTREE_EPS)
403{
404gtln[i]++;
405}
406}
407}
408for(int i = 0; i < 3; i++) {
409badness[i] = pow((double)ltln[i], 4) + pow((double)gtln[i], 4);
410}
411
412// Choose the least bad coordinate to split along.
413if(badness[0] < badness[1] && badness[0] < badness[2]) {
414n->which = 0;
415} else if(badness[1] < badness[2]) {
416n->which = 1;
417} else {
418n->which = 2;
419}
420n->c = ptAve.Element(n->which);
421
422if(totaln < 3 || totaln == gtln[n->which] || totaln == ltln[n->which]) {
423n->edges = sell;
424// and we're a leaf node
425return n;
426}
427
428// Sort the edges according to which side(s) of the split plane they're on.
429SEdgeLl *gtl = NULL, *ltl = NULL;
430for(flip = sell; flip; flip = flip->next) {
431if(flip->se->a.Element(n->which) < n->c + KDTREE_EPS ||
432flip->se->b.Element(n->which) < n->c + KDTREE_EPS)
433{
434SEdgeLl *selln = SEdgeLl::Alloc();
435selln->se = flip->se;
436selln->next = ltl;
437ltl = selln;
438}
439if(flip->se->a.Element(n->which) > n->c - KDTREE_EPS ||
440flip->se->b.Element(n->which) > n->c - KDTREE_EPS)
441{
442SEdgeLl *selln = SEdgeLl::Alloc();
443selln->se = flip->se;
444selln->next = gtl;
445gtl = selln;
446}
447}
448
449n->lt = SKdNodeEdges::From(ltl);
450n->gt = SKdNodeEdges::From(gtl);
451return n;
452}
453
454int SKdNodeEdges::AnyEdgeCrossings(Vector a, Vector b, int cnt,
455Vector *pi, SPointList *spl) const
456{
457int inters = 0;
458if(gt && lt) {
459if(a.Element(which) < c + KDTREE_EPS ||
460b.Element(which) < c + KDTREE_EPS)
461{
462inters += lt->AnyEdgeCrossings(a, b, cnt, pi, spl);
463}
464if(a.Element(which) > c - KDTREE_EPS ||
465b.Element(which) > c - KDTREE_EPS)
466{
467inters += gt->AnyEdgeCrossings(a, b, cnt, pi, spl);
468}
469} else {
470SEdgeLl *sell;
471for(sell = edges; sell; sell = sell->next) {
472SEdge *se = sell->se;
473if(se->tag == cnt) continue;
474if(se->EdgeCrosses(a, b, pi, spl)) {
475inters++;
476}
477se->tag = cnt;
478}
479}
480return inters;
481}
482
483//-----------------------------------------------------------------------------
484// We have an edge list that contains only collinear edges, maybe with more
485// splits than necessary. Merge any collinear segments that join.
486//-----------------------------------------------------------------------------
487void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
488const Vector lineStart = a;
489const Vector lineDirection = b.Minus(a);
490std::sort(l.begin(), l.end(), [&](const SEdge &a, const SEdge &b) {
491double ta = (a.a.Minus(lineStart)).DivProjected(lineDirection);
492double tb = (b.a.Minus(lineStart)).DivProjected(lineDirection);
493
494return (ta < tb);
495});
496
497l.ClearTags();
498SEdge *prev = nullptr;
499for(auto &now : l) {
500if(prev != nullptr) {
501if((prev->b).Equals(now.a) && prev->auxA == now.auxA) {
502// The previous segment joins up to us; so merge it into us.
503prev->tag = 1;
504now.a = prev->a;
505}
506}
507prev = &now;
508}
509l.RemoveTagged();
510}
511
512void SPointList::Clear() {
513l.Clear();
514}
515
516bool SPointList::ContainsPoint(Vector pt) const {
517return (IndexForPoint(pt) >= 0);
518}
519
520int SPointList::IndexForPoint(Vector pt) const {
521int i;
522for(i = 0; i < l.n; i++) {
523const SPoint *p = &(l[i]);
524if(pt.Equals(p->p)) {
525return i;
526}
527}
528// Not found, so return negative to indicate that.
529return -1;
530}
531
532void SPointList::IncrementTagFor(Vector pt) {
533SPoint *p;
534for(p = l.First(); p; p = l.NextAfter(p)) {
535if(pt.Equals(p->p)) {
536(p->tag)++;
537return;
538}
539}
540SPoint pa;
541pa.p = pt;
542pa.tag = 1;
543l.Add(&pa);
544}
545
546void SPointList::Add(Vector pt) {
547SPoint p = {};
548p.p = pt;
549l.Add(&p);
550}
551
552void SContour::AddPoint(Vector p) {
553SPoint sp;
554sp.tag = 0;
555sp.p = p;
556
557l.Add(&sp);
558}
559
560void SContour::MakeEdgesInto(SEdgeList *el) const {
561int i;
562for(i = 0; i < (l.n - 1); i++) {
563el->AddEdge(l[i].p, l[i+1].p);
564}
565}
566
567void SContour::CopyInto(SContour *dest) const {
568for(const SPoint *sp = l.First(); sp; sp = l.NextAfter(sp)) {
569dest->AddPoint(sp->p);
570}
571}
572
573void SContour::FindPointWithMinX() {
574xminPt = Vector::From(1e10, 1e10, 1e10);
575for(const SPoint *sp = l.First(); sp; sp = l.NextAfter(sp)) {
576if(sp->p.x < xminPt.x) {
577xminPt = sp->p;
578}
579}
580}
581
582Vector SContour::ComputeNormal() const {
583Vector n = Vector::From(0, 0, 0);
584
585for(int i = 0; i < l.n - 2; i++) {
586Vector u = (l[i+1].p).Minus(l[i+0].p).WithMagnitude(1);
587Vector v = (l[i+2].p).Minus(l[i+1].p).WithMagnitude(1);
588Vector nt = u.Cross(v);
589if(nt.Magnitude() > n.Magnitude()) {
590n = nt;
591}
592}
593return n.WithMagnitude(1);
594}
595
596Vector SContour::AnyEdgeMidpoint() const {
597ssassert(l.n >= 2, "Need two points to find a midpoint");
598return ((l[0].p).Plus(l[1].p)).ScaledBy(0.5);
599}
600
601bool SContour::IsClockwiseProjdToNormal(Vector n) const {
602// Degenerate things might happen as we draw; doesn't really matter
603// what we do then.
604if(n.Magnitude() < 0.01) return true;
605
606return (SignedAreaProjdToNormal(n) < 0);
607}
608
609double SContour::SignedAreaProjdToNormal(Vector n) const {
610// An arbitrary 2d coordinate system that has n as its normal
611Vector u = n.Normal(0);
612Vector v = n.Normal(1);
613
614double area = 0;
615for(int i = 0; i < (l.n - 1); i++) {
616double u0 = (l[i ].p).Dot(u);
617double v0 = (l[i ].p).Dot(v);
618double u1 = (l[i+1].p).Dot(u);
619double v1 = (l[i+1].p).Dot(v);
620
621area += ((v0 + v1)/2)*(u1 - u0);
622}
623return area;
624}
625
626bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) const {
627Vector u = n.Normal(0);
628Vector v = n.Normal(1);
629
630double up = p.Dot(u);
631double vp = p.Dot(v);
632
633bool inside = false;
634for(int i = 0; i < (l.n - 1); i++) {
635double ua = (l[i ].p).Dot(u);
636double va = (l[i ].p).Dot(v);
637// The curve needs to be exactly closed; approximation is death.
638double ub = (l[(i+1)%(l.n-1)].p).Dot(u);
639double vb = (l[(i+1)%(l.n-1)].p).Dot(v);
640
641if ((((va <= vp) && (vp < vb)) ||
642((vb <= vp) && (vp < va))) &&
643(up < (ub - ua) * (vp - va) / (vb - va) + ua))
644{
645inside = !inside;
646}
647}
648
649return inside;
650}
651
652void SContour::Reverse() {
653l.Reverse();
654}
655
656
657void SPolygon::Clear() {
658int i;
659for(i = 0; i < l.n; i++) {
660(l[i]).l.Clear();
661}
662l.Clear();
663}
664
665void SPolygon::AddEmptyContour() {
666SContour c = {};
667l.Add(&c);
668}
669
670void SPolygon::MakeEdgesInto(SEdgeList *el) const {
671int i;
672for(i = 0; i < l.n; i++) {
673(l[i]).MakeEdgesInto(el);
674}
675}
676
677Vector SPolygon::ComputeNormal() const {
678if(l.IsEmpty())
679return Vector::From(0, 0, 0);
680return (l[0]).ComputeNormal();
681}
682
683double SPolygon::SignedArea() const {
684double area = 0;
685// This returns the true area only if the contours are all oriented
686// correctly, with the holes backwards from the outer contours.
687for(const SContour *sc = l.First(); sc; sc = l.NextAfter(sc)) {
688area += sc->SignedAreaProjdToNormal(normal);
689}
690return area;
691}
692
693bool SPolygon::ContainsPoint(Vector p) const {
694return (WindingNumberForPoint(p) % 2) == 1;
695}
696
697size_t SPolygon::WindingNumberForPoint(Vector p) const {
698auto winding = std::count_if(l.begin(), l.end(), [&](const SContour &sc) {
699return sc.ContainsPointProjdToNormal(normal, p);
700});
701return winding;
702}
703
704void SPolygon::FixContourDirections() {
705// At output, the contour's tag will be 1 if we reversed it, else 0.
706l.ClearTags();
707
708// Outside curve looks counterclockwise, projected against our normal.
709int i, j;
710for(i = 0; i < l.n; i++) {
711SContour *sc = &(l[i]);
712if(sc->l.n < 2) continue;
713// The contours may not intersect, but they may share vertices; so
714// testing a vertex for point-in-polygon may fail, but the midpoint
715// of an edge is okay.
716Vector pt = (((sc->l[0]).p).Plus(sc->l[1].p)).ScaledBy(0.5);
717
718sc->timesEnclosed = 0;
719bool outer = true;
720for(j = 0; j < l.n; j++) {
721if(i == j) continue;
722SContour *sct = &(l[j]);
723if(sct->ContainsPointProjdToNormal(normal, pt)) {
724outer = !outer;
725(sc->timesEnclosed)++;
726}
727}
728
729bool clockwise = sc->IsClockwiseProjdToNormal(normal);
730if((clockwise && outer) || (!clockwise && !outer)) {
731sc->Reverse();
732sc->tag = 1;
733}
734}
735}
736
737bool SPolygon::IsEmpty() const {
738if(l.IsEmpty() || l[0].l.IsEmpty())
739return true;
740return false;
741}
742
743Vector SPolygon::AnyPoint() const {
744ssassert(!IsEmpty(), "Need at least one point");
745return l[0].l[0].p;
746}
747
748bool SPolygon::SelfIntersecting(Vector *intersectsAt) const {
749SEdgeList el = {};
750MakeEdgesInto(&el);
751SKdNodeEdges *kdtree = SKdNodeEdges::From(&el);
752
753int cnt = 1;
754el.l.ClearTags();
755
756bool ret = false;
757SEdge *se;
758for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
759int inters = kdtree->AnyEdgeCrossings(se->a, se->b, cnt, intersectsAt);
760if(inters != 1) {
761ret = true;
762break;
763}
764cnt++;
765}
766
767el.Clear();
768return ret;
769}
770
771void SPolygon::InverseTransformInto(SPolygon *sp, Vector u, Vector v, Vector n) const {
772for(const SContour &sc : l) {
773SContour tsc = {};
774tsc.timesEnclosed = sc.timesEnclosed;
775for(const SPoint &sp : sc.l) {
776tsc.AddPoint(sp.p.DotInToCsys(u, v, n));
777}
778sp->l.Add(&tsc);
779}
780}
781
782//-----------------------------------------------------------------------------
783// Low-quality routines to cutter radius compensate a polygon. Assumes the
784// polygon is in the xy plane, and the contours all go in the right direction
785// with respect to normal (0, 0, -1).
786//-----------------------------------------------------------------------------
787void SPolygon::OffsetInto(SPolygon *dest, double r) const {
788int i;
789dest->Clear();
790for(i = 0; i < l.n; i++) {
791const SContour *sc = &(l[i]);
792dest->AddEmptyContour();
793sc->OffsetInto(&(dest->l[dest->l.n-1]), r);
794}
795}
796//-----------------------------------------------------------------------------
797// Calculate the intersection point of two lines. Returns true for success,
798// false if they're parallel.
799//-----------------------------------------------------------------------------
800static bool IntersectionOfLines(double x0A, double y0A, double dxA, double dyA,
801double x0B, double y0B, double dxB, double dyB,
802double *xi, double *yi)
803{
804double A[2][2];
805double b[2];
806
807// The line is given to us in the form:
808// (x(t), y(t)) = (x0, y0) + t*(dx, dy)
809// so first rewrite it as
810// (x - x0, y - y0) dot (dy, -dx) = 0
811// x*dy - x0*dy - y*dx + y0*dx = 0
812// x*dy - y*dx = (x0*dy - y0*dx)
813
814// So write the matrix, pre-pivoted.
815if(fabs(dyA) > fabs(dyB)) {
816A[0][0] = dyA; A[0][1] = -dxA; b[0] = x0A*dyA - y0A*dxA;
817A[1][0] = dyB; A[1][1] = -dxB; b[1] = x0B*dyB - y0B*dxB;
818} else {
819A[1][0] = dyA; A[1][1] = -dxA; b[1] = x0A*dyA - y0A*dxA;
820A[0][0] = dyB; A[0][1] = -dxB; b[0] = x0B*dyB - y0B*dxB;
821}
822
823// Check the determinant; if it's zero then no solution.
824if(fabs(A[0][0]*A[1][1] - A[0][1]*A[1][0]) < LENGTH_EPS) {
825return false;
826}
827
828// Solve
829double v = A[1][0] / A[0][0];
830A[1][0] -= A[0][0]*v;
831A[1][1] -= A[0][1]*v;
832b[1] -= b[0]*v;
833
834// Back-substitute.
835*yi = b[1] / A[1][1];
836*xi = (b[0] - A[0][1]*(*yi)) / A[0][0];
837
838return true;
839}
840void SContour::OffsetInto(SContour *dest, double r) const {
841int i;
842
843for(i = 0; i < l.n; i++) {
844Vector a, b, c;
845Vector dp, dn;
846double thetan, thetap;
847
848a = l[WRAP(i-1, (l.n-1))].p;
849b = l[WRAP(i, (l.n-1))].p;
850c = l[WRAP(i+1, (l.n-1))].p;
851
852dp = a.Minus(b);
853thetap = atan2(dp.y, dp.x);
854
855dn = b.Minus(c);
856thetan = atan2(dn.y, dn.x);
857
858// A short line segment in a badly-generated polygon might look
859// okay but screw up our sense of direction.
860if(dp.Magnitude() < LENGTH_EPS || dn.Magnitude() < LENGTH_EPS) {
861continue;
862}
863
864if(thetan > thetap && (thetan - thetap) > PI) {
865thetap += 2*PI;
866}
867if(thetan < thetap && (thetap - thetan) > PI) {
868thetan += 2*PI;
869}
870
871if(fabs(thetan - thetap) < (1*PI)/180) {
872Vector p = { b.x - r*sin(thetap), b.y + r*cos(thetap), 0 };
873dest->AddPoint(p);
874} else if(thetan < thetap) {
875// This is an inside corner. We have two edges, Ep and En. Move
876// out from their intersection by radius, normal to En, and
877// then draw a line parallel to En. Move out from their
878// intersection by radius, normal to Ep, and then draw a second
879// line parallel to Ep. The point that we want to generate is
880// the intersection of these two lines--it removes as much
881// material as we can without removing any that we shouldn't.
882double px0, py0, pdx, pdy;
883double nx0, ny0, ndx, ndy;
884double x = 0.0, y = 0.0;
885
886px0 = b.x - r*sin(thetap);
887py0 = b.y + r*cos(thetap);
888pdx = cos(thetap);
889pdy = sin(thetap);
890
891nx0 = b.x - r*sin(thetan);
892ny0 = b.y + r*cos(thetan);
893ndx = cos(thetan);
894ndy = sin(thetan);
895
896IntersectionOfLines(px0, py0, pdx, pdy,
897nx0, ny0, ndx, ndy,
898&x, &y);
899
900dest->AddPoint(Vector::From(x, y, 0));
901} else {
902if(fabs(thetap - thetan) < (6*PI)/180) {
903Vector pp = { b.x - r*sin(thetap),
904b.y + r*cos(thetap), 0 };
905dest->AddPoint(pp);
906
907Vector pn = { b.x - r*sin(thetan),
908b.y + r*cos(thetan), 0 };
909dest->AddPoint(pn);
910} else {
911double theta;
912for(theta = thetap; theta <= thetan; theta += (6*PI)/180) {
913Vector p = { b.x - r*sin(theta),
914b.y + r*cos(theta), 0 };
915dest->AddPoint(p);
916}
917}
918}
919}
920}
921
922