solvespace/src/polyline.cpp

245 lines
6.7 KiB
C++

//-----------------------------------------------------------------------------
// A helper class to assemble scattered edges into contiguous polylines,
// as nicely as possible.
//
// Copyright 2016 M-Labs Ltd
//-----------------------------------------------------------------------------
#include "solvespace.h"
bool PolylineBuilder::Vertex::GetNext(uint32_t kind, Vertex **next, Edge **nextEdge) {
auto it = std::find_if(edges.begin(), edges.end(), [&](const Edge *e) {
return e->tag == 0 && e->kind == kind;
});
if(it != edges.end()) {
(*it)->tag = 1;
*next = (*it)->GetOtherVertex(this);
*nextEdge = *it;
return true;
}
return false;
}
bool PolylineBuilder::Vertex::GetNext(uint32_t kind, Vector plane, double dist,
Vertex **next, Edge **nextEdge) {
Edge *best = NULL;
double minD = VERY_POSITIVE;
for(Edge *e : edges) {
if(e->tag != 0) continue;
if(e->kind != kind) continue;
// We choose the best next edge with minimal distance from the current plane
Vector nextPos = e->GetOtherVertex(this)->pos;
double curD = fabs(plane.Dot(nextPos) - dist);
if(best != NULL && curD > minD) continue;
best = e;
minD = curD;
}
if(best != NULL) {
best->tag = 1;
*next = best->GetOtherVertex(this);
*nextEdge = best;
return true;
}
return false;
}
size_t PolylineBuilder::Vertex::CountEdgesWithTagAndKind(int tag, uint32_t kind) const {
return std::count_if(edges.begin(), edges.end(), [&](const Edge *e) {
return e->tag == tag && e->kind == kind;
});
}
PolylineBuilder::Vertex *PolylineBuilder::Edge::GetOtherVertex(PolylineBuilder::Vertex *v) const {
if(a == v) return b;
if(b == v) return a;
return NULL;
}
size_t PolylineBuilder::VertexPairHash::operator()(const std::pair<Vertex *, Vertex *> &v) const {
return ((uintptr_t)v.first / sizeof(Vertex)) ^
((uintptr_t)v.second / sizeof(Vertex));
}
bool PolylineBuilder::Edge::GetStartAndNext(PolylineBuilder::Vertex **start,
PolylineBuilder::Vertex **next, bool loop) const {
size_t numA = a->CountEdgesWithTagAndKind(0, kind);
size_t numB = b->CountEdgesWithTagAndKind(0, kind);
if((numA == 1 && numB > 1) || (loop && numA > 1 && numB > 1)) {
*start = a;
*next = b;
return true;
}
if(numA > 1 && numB == 1) {
*start = b;
*next = a;
return true;
}
return false;
}
PolylineBuilder::~PolylineBuilder() {
Clear();
}
void PolylineBuilder::Clear() {
for(Edge *e : edges) {
delete e;
}
edges.clear();
for(auto &v : vertices) {
delete v.second;
}
vertices.clear();
}
PolylineBuilder::Vertex *PolylineBuilder::AddVertex(const Vector &pos) {
auto it = vertices.find(pos);
if(it != vertices.end()) {
return it->second;
}
Vertex *result = new Vertex;
result->pos = pos;
vertices.emplace(pos, result);
return result;
}
PolylineBuilder::Edge *PolylineBuilder::AddEdge(const Vector &p0, const Vector &p1,
uint32_t kind, uintptr_t data) {
Vertex *v0 = AddVertex(p0);
Vertex *v1 = AddVertex(p1);
if(v0 == v1) return NULL;
auto it = edgeMap.find(std::make_pair(v0, v1));
if(it != edgeMap.end()) {
return it->second;
}
PolylineBuilder::Edge *edge = new PolylineBuilder::Edge {};
edge->a = v0;
edge->b = v1;
edge->kind = kind;
edge->tag = 0;
edge->data = data;
edges.push_back(edge);
edgeMap.emplace(std::make_pair(v0, v1), edge);
v0->edges.push_back(edge);
v1->edges.push_back(edge);
return edge;
}
void PolylineBuilder::Generate(
std::function<void(Vertex *start, Vertex *next, Edge *edge)> startFunc,
std::function<void(Vertex *next, Edge *edge)> nextFunc,
std::function<void(Edge *alone)> aloneFunc,
std::function<void()> endFunc) {
bool found;
bool loop = false;
do {
found = false;
for(PolylineBuilder::Edge *e : edges) {
if(e->tag != 0) continue;
Vertex *start;
Vertex *next;
if(!e->GetStartAndNext(&start, &next, loop)) continue;
Vector startPos = start->pos;
Vector nextPos = next->pos;
found = true;
e->tag = 1;
startFunc(start, next, e);
Edge *nextEdge;
if(next->GetNext(e->kind, &next, &nextEdge)) {
Vector plane = nextPos.Minus(startPos).Cross(next->pos.Minus(startPos));
double dist = plane.Dot(startPos);
nextFunc(next, nextEdge);
while(next->GetNext(e->kind, plane, dist, &next, &nextEdge)) {
nextFunc(next, nextEdge);
}
}
endFunc();
}
if(!found && !loop) {
loop = true;
found = true;
}
} while(found);
for(PolylineBuilder::Edge *e : edges) {
if(e->tag != 0) continue;
aloneFunc(e);
}
}
void PolylineBuilder::MakeFromEdges(const SEdgeList &sel) {
for(const SEdge &se : sel.l) {
AddEdge(se.a, se.b, (uint32_t)se.auxA, reinterpret_cast<uintptr_t>(&se));
}
}
void PolylineBuilder::MakeFromOutlines(const SOutlineList &ol) {
for(const SOutline &so : ol.l) {
// Use outline tag as kind, so that emphasized and contour outlines
// would not be composed together.
AddEdge(so.a, so.b, (uint32_t)so.tag, reinterpret_cast<uintptr_t>(&so));
}
}
void PolylineBuilder::GenerateEdges(SEdgeList *sel) {
Vector prev;
auto startFunc = [&](Vertex *start, Vertex *next, Edge *e) {
sel->AddEdge(start->pos, next->pos, e->kind);
prev = next->pos;
};
auto nextFunc = [&](Vertex *next, Edge *e) {
sel->AddEdge(prev, next->pos, e->kind);
prev = next->pos;
};
auto aloneFunc = [&](Edge *e) {
sel->AddEdge(e->a->pos, e->b->pos, e->kind);
};
Generate(startFunc, nextFunc, aloneFunc);
}
void PolylineBuilder::GenerateOutlines(SOutlineList *sol) {
Vector prev;
auto startFunc = [&](Vertex *start, Vertex *next, Edge *e) {
SOutline *so = e->outline;
sol->AddEdge(start->pos, next->pos, so->nl, so->nr, so->tag);
prev = next->pos;
};
auto nextFunc = [&](Vertex *next, Edge *e) {
SOutline *so = e->outline;
sol->AddEdge(prev, next->pos, so->nl, so->nr, so->tag);
prev = next->pos;
};
auto aloneFunc = [&](Edge *e) {
SOutline *so = e->outline;
sol->AddEdge(so->a, so->b, so->nl, so->nr, so->tag);
};
Generate(startFunc, nextFunc, aloneFunc);
}