Files
secondo/Algebras/FixedMRegion/fmr/TraversedAreaFactory.cpp
2026-01-23 17:03:45 +08:00

426 lines
14 KiB
C++

/*
----
* This file is part of libfmr
*
* File: TraversedAreaFactory.cpp
* Author: Florian Heinz <fh@sysv.de>
*
* 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<Curve*> createCurves(Region& region, TransformationUnit&);
static void findIntersections(std::vector<Curve*>&);
static std::vector<std::vector<Curve *> > getPartitions(std::vector<Curve *>);
static std::vector<RCurve> getCycle(std::vector<Curve*> cs);
static std::pair<double,std::pair<double,Curve*> >getFirst(std::vector<Curve*>);
/*
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<Curve*> curves = createCurves(fmregion.region,
fmregion.trafos[i]);
// Find intersections between curves
findIntersections(curves);
// Divide the intersecting curves into disjunct partitons
std::vector<std::vector<Curve *> > partitions = getPartitions(curves);
for (int i = 0; i < partitions.size(); i++) {
// From each partiton create a cycle of RCurve border segments
std::vector<RCurve> 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<Curve *>::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<Curve*> createCurves(Region& region, TransformationUnit& tu){
std::vector<Curve*> 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<Curve*>& curves) {
std::vector<Curve *>::iterator c1 = curves.begin(), c2;
while (c1 != curves.end()) {
c2 = std::vector<Curve *>::iterator(c1);
while (c2 != curves.end()) {
Trochoid *t1 = dynamic_cast<Trochoid*> (*c1), *t2 =
dynamic_cast<Trochoid*> (*c2);
Ravdoid *r1 = dynamic_cast<Ravdoid*> (*c1), *r2 =
dynamic_cast<Ravdoid *> (*c2);
SegT *s1 = dynamic_cast<SegT*> (*c1), *s2 =
dynamic_cast<SegT *> (*c2);
std::vector<Point> 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<std::vector<Curve *> > getPartitions(std::vector<Curve *>cs){
std::vector<std::vector<Curve *> > ret;
while (cs.size() > 0) {
Curve *c = *(cs.begin());
cs.erase(cs.begin());
if (c->getAdjacentCurves().size() == 0) {
continue;
}
std::vector<Curve*> 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<Curve *> 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<Curve*>::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<RCurve> getCycle(std::vector<Curve*> cs) {
// Find a suitable start segment
std::pair<double, std::pair<double, Curve*> > p = getFirst(cs);
std::vector<RCurve> 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<double, std::pair<double, Curve*> > getFirstOld(
std::vector<Curve*> cs) {
Trochoid *c = NULL;
// Find trochoid c with the biggest b-value
for (int i = 0; i < cs.size(); i++) {
Trochoid* o = dynamic_cast<Trochoid*> (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<double, double> 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<double, std::pair<double, Curve*> > getFirst(
std::vector<Curve*> 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<double, double> 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);
}