
Before this commit, the outlines are generated in an arbitrary order from the kd-tree. This worked just fine for continuous lines, but for stippling, especially on curves, this meant that most of the piecewise linear edges would have the stippling phase restart from zero, leading to a very poor appearance.
245 lines
6.7 KiB
C++
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);
|
|
}
|