/* ---- * This file is part of libfmr * * File: TraversedAreaFactory.cpp * Author: Florian Heinz * * Created on September 16, 2016, 10:48 AM ---- //paragraph [1] Title: [{\Large \bf \begin {center}] [\end {center}}] //[TOC] [\tableofcontents] [1] Implementation of the operation ~traversedArea~ [TOC] 1 Overview The operation ~traversedArea~ creates the traversed area from a FMRegion and a TransformationUnit in several steps. - First, the curves are created from the TransformationUnit ~tu~ and the FMRegion ~fmregion~. - Then, the intersections between these curves are calculated and stored in the curves. - The partitions of intersecting curves are calculated if the areas are non-contiguous. - After that, the border cycles of these partitions are determined and converted to RCurve segments. - Finally, a CRegion made up from RFaces is returned, which represents the traversed area. */ #include "fmr_TraversedAreaFactory.h" using namespace fmr; // Declarations CRegion fmr::traversedArea(FMRegion& fmregion); static std::vector createCurves(Region& region, TransformationUnit&); static void findIntersections(std::vector&); static std::vector > getPartitions(std::vector); static std::vector getCycle(std::vector cs); static std::pair >getFirst(std::vector); /* 2 ~traversedArea~ Main function to calculate the traversed region of FMRegion ~fmregion~. */ CRegion fmr::traversedArea(FMRegion& fmregion) { CRegion ret; // Iterate over all transformations for (int i = 0; i < fmregion.trafos.size(); i++) { // Create the necessary curves std::vector curves = createCurves(fmregion.region, fmregion.trafos[i]); // Find intersections between curves findIntersections(curves); // Divide the intersecting curves into disjunct partitons std::vector > partitions = getPartitions(curves); for (int i = 0; i < partitions.size(); i++) { // From each partiton create a cycle of RCurve border segments std::vector ta = getCycle(partitions[i]); RFace rf; rf.face = ta; // Store this RFace into this CRegion ret.faces.push_back(rf); } // Free all curves std::vector::iterator it = curves.begin(); while (it != curves.end()) { delete (*it); it++; } } return ret; } /* 3 ~createCurves~ Takes a TransformationUnit ~tu~ and creates the curve objects described by the FMRegion under this transformation. For each segment, the following curves are generated: - Segment at start instant - Segment at end instant - Trochoid described by the start point during the time interval - Ravdoid described by the segment during the time interval */ static std::vector createCurves(Region& region, TransformationUnit& tu){ std::vector curves; for (int nrface = 0; nrface < region.faces.size(); nrface++) { Face& mainface = region.faces[nrface]; for (int nrcycle = -1; nrcycle < (int)mainface.holes.size(); nrcycle++){ Face& face = (nrcycle < 0) ? mainface : mainface.holes[nrcycle]; for (int nrseg = 0; nrseg < face.segs.size(); nrseg++) { Seg seg = face.segs[nrseg]; Trochoid *tr = new Trochoid(Trochoid::create(seg.i, tu)); Ravdoid *rd = new Ravdoid(Ravdoid::create(seg, tu)); SegT *si = new SegT(SegT::create(seg, tu, true)); SegT *sf = new SegT(SegT::create(seg, tu, false)); if (tr->b < PRECISION) { curves.push_back( new SegT(Seg(tr->project(0), tr->project(1)))); delete tr; } else { curves.push_back(tr); } if (rd->tmin1 == 0 && rd->tmin2 == 0) delete rd; else curves.push_back(rd); curves.push_back(si); curves.push_back(sf); } } } return curves; } /* 4 ~findIntersections~ Find all intersections in a set of curves. Stores the intersection points together with the peer curve in the curve. */ static void findIntersections(std::vector& curves) { std::vector::iterator c1 = curves.begin(), c2; while (c1 != curves.end()) { c2 = std::vector::iterator(c1); while (c2 != curves.end()) { Trochoid *t1 = dynamic_cast (*c1), *t2 = dynamic_cast (*c2); Ravdoid *r1 = dynamic_cast (*c1), *r2 = dynamic_cast (*c2); SegT *s1 = dynamic_cast (*c1), *s2 = dynamic_cast (*c2); std::vector it; if (false) { // Dummy entry } else if (t1 && t2) { // Intersection between two trochoids. The trochoid with // the higher b-value should be the first parameter, since // the calculation is numerically more stable then. if (t1->b > t2->b) it = ISTrochoids(*t1, *t2).findIntersection(); else it = ISTrochoids(*t2, *t1).findIntersection(); } else if (t1 && r2) { // Intersection between trochoid and ravdoid it = ISTrocRavd(*t1, *r2).findIntersection(); } else if (r1 && t2) { // Intersection between trochoid and ravdoid it = ISTrocRavd(*t2, *r1).findIntersection(); } else if (r1 && r2) { // Intersection between two ravdoids it = ISRavdoids(*r1, *r2).findIntersection(); } else if ((t1 || r1) && s2) { // Intersection between a segment and a curve it = ISSegCurve(*s2, **c1).findIntersection(); } else if (s1 && (t2 || r2)) { // Intersection between a segment and a curve it = ISSegCurve(*s1, **c2).findIntersection(); } else if (s1 && s2 && s1 != s2) { // Intersection between two segments // A self-intersection cannot happen here it = ISSegCurve(*s1, *s2).findIntersection(); } c2++; } c1++; } } /* 5 ~getPartitions~ Divide the intersecting curves into disjunct partitions. */ static std::vector > getPartitions(std::vectorcs){ std::vector > ret; while (cs.size() > 0) { Curve *c = *(cs.begin()); cs.erase(cs.begin()); if (c->getAdjacentCurves().size() == 0) { continue; } std::vector partition; partition.push_back(c); int added; // Iterate and add all adjacent curves (and the curves adjacent to // those) until no more curves were added do { added = 0; std::vector newp; for (int i = 0; i < partition.size(); i++) { Curve *t = partition[i]; for (int j = 0; j < t->getAdjacentCurves().size(); j++) { Curve *adj = t->getAdjacentCurves()[j]; if (std::find(partition.begin(), partition.end(), adj) == partition.end() && std::find(newp.begin(), newp.end(), adj) == newp.end()) { newp.push_back(adj); std::vector::iterator it = std::find(cs.begin(), cs.end(), adj); if (it != cs.end()) cs.erase(it); added++; } } } partition.insert(partition.end(), newp.begin(), newp.end()); } while (added > 0); ret.push_back(partition); } return ret; } /* 6 ~getCycle~ Creates a cycle of ~RCurve~ segments (see RCurve.cpp) from a set of curves. The intersections have to be calculated first (see ~findIntersections()~. The resulting set of RCurve segments construct the borderline of the set of intersecting curves ~cs~. */ static std::vector getCycle(std::vector cs) { // Find a suitable start segment std::pair > p = getFirst(cs); std::vector ta; Point start; int i = 1000; // 1000 iterations are maximum do { // The current curve Curve *cc = p.second.second; // The start instant of the curve segment double t1 = p.second.first; // The end instant of the curve segment double t2 = p.first; if (!cc) { // No successor curve element found. This is an error. ta.clear(); return ta; } Point p1 = cc->project(t1); Point p2 = cc->project(t2); // std::cerr << p1.ToString() << " - " << p2.ToString() << std::endl; if (!start.valid()) start = p2; else if (p2.near(start)) break; // The cycle is closed now! // Create an RCurve segment of the current curve from // instant t1 to t2 and add it to the cycle ta.push_back(cc->rcurve(t1, t2)); // Determine the next curve segment of the border cycle p = cc->getNext(t1, t2); } while (i-- > 0); return ta; } /* 7 ~getFirst~ Try to find a suitable curve segment to start the construction of the border cycle. This curve segment has to be part of the border cycle and the orientation has to be correct (counterclockwise). This approach determines the Trochoid with the biggest ~b~-parameter. If we have a full cycle (or the correct part of it),a point on that trochoid has the lowest or highest y-coordinate and is thus part of the border. If the rotation angle is too small, assume that the part from an endpoint of that trochoid to the next intersection is part of the cycle. */ #if 0 static std::pair > getFirstOld( std::vector cs) { Trochoid *c = NULL; // Find trochoid c with the biggest b-value for (int i = 0; i < cs.size(); i++) { Trochoid* o = dynamic_cast (cs[i]); if (o && (!c || c->b < o->b)) c = o; } int index = 0; double tt = 0; double pd = 2 * M_PI / c->rot; // t1 is the time of the lowest y-coordinate of trochoid c double t1 = (-c->toff - M_PI) / c->rot, t2, t3, t4; while (t1 < 0) { t1 += pd; } if (t1 > 1) { t1 -= pd; } t2 = t1 + pd / 2; t3 = t2 + pd / 2; t4 = t3 + pd / 2; if (t1 > 0 && t1 < 1) { tt = t1; index = 1; } else if (t2 > 0 && t2 < 1) { tt = t2; index = c->b > c->a ? 1 : 0; } else if (t1 < 0 && 0 < t2) { tt = 0; if ((t1 + t2) / 2 < 0) { index = c->b > c->a ? 1 : 0; } else { index = 1; } } else if (t2 < 0 && 0 < t3) { tt = 0; if ((t2 + t3) / 2 < 0) { index = 1; } else { index = c->b > c->a ? 1 : 0; } } else if (t3 < 0 && 0 < t4) { tt = 0; if ((t3 + t4) / 2 < 0) { index = c->b > c->a ? 1 : 0; } else { index = 1; } } // Find the next intersection point of the segment std::pair tx = c->getAdjacentIntersectionTimes(tt); double tt2 = (index == 0) ? tx.first : tx.second; Point start = c->project(tt2); // Return the succeeding rcurve segment, which is also part of the // cycle if c@[t1;t2] is part of the cycle return c->getNext(tt, tt2); } #endif static std::pair > getFirst( std::vector cs) { double bt = nan(""); Curve *bc = NULL; Point bp; #define DELTA 0.0001 for (unsigned int i = 0; i < cs.size(); i++) { Curve *c = cs[i]; for (double t = 0; t <= 1.0; t += DELTA) { if (!c->valid(t)) continue; Point p = c->project(t); if (!bc || p.x < bp.x) { bp = p; bc = c; bt = t; } } } double ba = nan(""); for (unsigned int i = 0; i < cs.size(); i++) { double a2, bt2; Curve *c = cs[i]; if (c->project(0.0).near(bp)) { Point p1 = c->project(0.0); Point p2 = c->project(0.000001); a2 = atan2(p2.y-p1.y, p2.x-p1.x); bt2 = 0.000001; } else if (c->project(1.0).near(bp)) { Point p1 = c->project(1.0); Point p2 = c->project(1.0-0.000001); a2 = atan2(p2.y-p1.y, p2.x-p1.x); bt2 = 1.0-0.000001; } else continue; // std::cerr << "Curve " << c->ToString() << " has angle " << a2 << std::endl; if (std::isnan(ba) || ba > a2) { ba = a2; bc = c; bt = bt2; } } std::pair tx = bc->getAdjacentIntersectionTimes(bt); double t1, t2; double y1 = bc->fy(bt); double y2 = bc->fy(bt+0.0001); if (y1 > y2) { t1 = tx.first; t2 = tx.second; } else { t1 = tx.second; t2 = tx.first; } // std::cerr << "Start: " << bc->ToString() << std::endl; // Point p1 = bc->project(t1); // Point p2 = bc->project(t2); // std::cerr << p1.ToString() << " - " << p2.ToString() << std::endl; return bc->getNext(t1, t2); }