Factor out PolylineBuilder from DXF export code.
This commit is contained in:
parent
d2c250324b
commit
186911a51a
|
@ -143,6 +143,7 @@ set(solvespace_cad_SOURCES
|
||||||
mesh.cpp
|
mesh.cpp
|
||||||
modify.cpp
|
modify.cpp
|
||||||
mouse.cpp
|
mouse.cpp
|
||||||
|
polyline.cpp
|
||||||
polygon.cpp
|
polygon.cpp
|
||||||
resource.cpp
|
resource.cpp
|
||||||
request.cpp
|
request.cpp
|
||||||
|
|
|
@ -107,6 +107,14 @@ public:
|
||||||
Vector4 Project4d() const;
|
Vector4 Project4d() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VectorHash {
|
||||||
|
size_t operator()(const Vector &v) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VectorPred {
|
||||||
|
bool operator()(Vector a, Vector b) const;
|
||||||
|
};
|
||||||
|
|
||||||
class Vector4 {
|
class Vector4 {
|
||||||
public:
|
public:
|
||||||
double w, x, y, z;
|
double w, x, y, z;
|
||||||
|
|
|
@ -6,134 +6,6 @@
|
||||||
#include <libdxfrw.h>
|
#include <libdxfrw.h>
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
class PolylineBuilder {
|
|
||||||
public:
|
|
||||||
struct Edge;
|
|
||||||
|
|
||||||
struct Vertex {
|
|
||||||
Vector pos;
|
|
||||||
std::vector<Edge *> edges;
|
|
||||||
|
|
||||||
bool getNext(hStyle hs, Vertex **next) {
|
|
||||||
auto it = std::find_if(edges.begin(), edges.end(), [&](const Edge *e) {
|
|
||||||
return e->tag == 0 && e->style.v == hs.v;
|
|
||||||
});
|
|
||||||
if(it != edges.end()) {
|
|
||||||
(*it)->tag = 1;
|
|
||||||
*next = (*it)->getOtherVertex(this);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t countEdgesWithTagAndStyle(int tag, hStyle hs) const {
|
|
||||||
return std::count_if(edges.begin(), edges.end(), [&](const Edge *e) {
|
|
||||||
return e->tag == tag && e->style.v == hs.v;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Edge {
|
|
||||||
Vertex *a;
|
|
||||||
Vertex *b;
|
|
||||||
hStyle style;
|
|
||||||
int tag;
|
|
||||||
|
|
||||||
Vertex *getOtherVertex(Vertex *v) {
|
|
||||||
if(a == v) return b;
|
|
||||||
if(b == v) return a;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getStartAndNext(Vertex **start, Vertex **next, bool loop) {
|
|
||||||
size_t numA = a->countEdgesWithTagAndStyle(0, style);
|
|
||||||
size_t numB = b->countEdgesWithTagAndStyle(0, style);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VectorHash {
|
|
||||||
size_t operator()(const Vector &v) const {
|
|
||||||
static const size_t size = std::numeric_limits<size_t>::max() / 2 - 1;
|
|
||||||
static const double eps = (4.0 * LENGTH_EPS);
|
|
||||||
|
|
||||||
double x = fabs(v.x) / eps;
|
|
||||||
double y = fabs(v.y) / eps;
|
|
||||||
|
|
||||||
size_t xs = size_t(fmod(x, double(size)));
|
|
||||||
size_t ys = size_t(fmod(y, double(size)));
|
|
||||||
|
|
||||||
return ys * size + xs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VectorPred {
|
|
||||||
bool operator()(Vector a, Vector b) const {
|
|
||||||
return a.Equals(b, LENGTH_EPS);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<Vector, Vertex *, VectorHash, VectorPred> vertices;
|
|
||||||
std::vector<Edge *> edges;
|
|
||||||
|
|
||||||
~PolylineBuilder() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
for(Edge *e : edges) {
|
|
||||||
delete e;
|
|
||||||
}
|
|
||||||
edges.clear();
|
|
||||||
|
|
||||||
for(auto &v : vertices) {
|
|
||||||
delete v.second;
|
|
||||||
}
|
|
||||||
vertices.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex *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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Edge *addEdge(const Vector &p0, const Vector &p1, int style) {
|
|
||||||
Vertex *v0 = addVertex(p0);
|
|
||||||
Vertex *v1 = addVertex(p1);
|
|
||||||
if(v0 == v1) return NULL;
|
|
||||||
|
|
||||||
Edge *edge = new Edge { v0, v1, hStyle { (uint32_t)style }, 0 };
|
|
||||||
edges.push_back(edge);
|
|
||||||
|
|
||||||
v0->edges.push_back(edge);
|
|
||||||
v1->edges.push_back(edge);
|
|
||||||
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Routines for DXF export
|
// Routines for DXF export
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -245,43 +117,36 @@ public:
|
||||||
for(DxfFileWriter::BezierPath &path : writer->paths) {
|
for(DxfFileWriter::BezierPath &path : writer->paths) {
|
||||||
for(SBezier *sb : path.beziers) {
|
for(SBezier *sb : path.beziers) {
|
||||||
if(sb->deg != 1) continue;
|
if(sb->deg != 1) continue;
|
||||||
builder.addEdge(sb->ctrl[0], sb->ctrl[1], sb->auxA);
|
builder.AddEdge(sb->ctrl[0], sb->ctrl[1], (uint32_t)sb->auxA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found;
|
DRW_LWPolyline polyline;
|
||||||
bool loop = false;
|
|
||||||
do {
|
|
||||||
found = false;
|
|
||||||
for(PolylineBuilder::Edge *e : builder.edges) {
|
|
||||||
if(e->tag != 0) continue;
|
|
||||||
|
|
||||||
PolylineBuilder::Vertex *start;
|
auto startFunc = [&](PolylineBuilder::Vertex *start,
|
||||||
PolylineBuilder::Vertex *next;
|
PolylineBuilder::Vertex *next,
|
||||||
if(!e->getStartAndNext(&start, &next, loop)) continue;
|
PolylineBuilder::Edge *e) {
|
||||||
found = true;
|
hStyle hs = { e->kind };
|
||||||
e->tag = 1;
|
polyline = {};
|
||||||
|
assignEntityDefaults(&polyline, hs);
|
||||||
|
polyline.vertlist.push_back(new DRW_Vertex2D(start->pos.x, start->pos.y, 0.0));
|
||||||
|
polyline.vertlist.push_back(new DRW_Vertex2D(next->pos.x, next->pos.y, 0.0));
|
||||||
|
};
|
||||||
|
|
||||||
DRW_LWPolyline polyline;
|
auto nextFunc = [&](PolylineBuilder::Vertex *next, PolylineBuilder::Edge *e) {
|
||||||
assignEntityDefaults(&polyline, e->style);
|
polyline.vertlist.push_back(new DRW_Vertex2D(next->pos.x, next->pos.y, 0.0));
|
||||||
polyline.vertlist.push_back(new DRW_Vertex2D(start->pos.x, start->pos.y, 0.0));
|
};
|
||||||
polyline.vertlist.push_back(new DRW_Vertex2D(next->pos.x, next->pos.y, 0.0));
|
|
||||||
while(next->getNext(e->style, &next)) {
|
|
||||||
polyline.vertlist.push_back(new DRW_Vertex2D(next->pos.x, next->pos.y, 0.0));
|
|
||||||
}
|
|
||||||
dxf->writeLWPolyline(&polyline);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!found && !loop) {
|
auto endFunc = [&]() {
|
||||||
loop = true;
|
dxf->writeLWPolyline(&polyline);
|
||||||
found = true;
|
};
|
||||||
}
|
|
||||||
} while(found);
|
|
||||||
|
|
||||||
for(PolylineBuilder::Edge *e : builder.edges) {
|
auto aloneFunc = [&](PolylineBuilder::Edge *e) {
|
||||||
if(e->tag != 0) continue;
|
hStyle hs = { e->kind };
|
||||||
writeLine(e->a->pos, e->b->pos, e->style);
|
writeLine(e->a->pos, e->b->pos, hs);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
builder.Generate(startFunc, nextFunc, aloneFunc, endFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeEntities() override {
|
void writeEntities() override {
|
||||||
|
|
|
@ -349,5 +349,54 @@ public:
|
||||||
void SnapToVertex(Vector v, SMesh *extras);
|
void SnapToVertex(Vector v, SMesh *extras);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PolylineBuilder {
|
||||||
|
public:
|
||||||
|
struct Edge;
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
Vector pos;
|
||||||
|
std::vector<Edge *> edges;
|
||||||
|
|
||||||
|
bool GetNext(uint32_t kind, Vertex **next, Edge **nextEdge);
|
||||||
|
bool GetNext(uint32_t kind, Vector plane, double d, Vertex **next, Edge **nextEdge);
|
||||||
|
size_t CountEdgesWithTagAndKind(int tag, uint32_t kind) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexPairHash {
|
||||||
|
size_t operator()(const std::pair<Vertex *, Vertex *> &v) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Edge {
|
||||||
|
Vertex *a;
|
||||||
|
Vertex *b;
|
||||||
|
uint32_t kind;
|
||||||
|
int tag;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uintptr_t data;
|
||||||
|
SOutline *outline;
|
||||||
|
SEdge *edge;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vertex *GetOtherVertex(Vertex *v) const;
|
||||||
|
bool GetStartAndNext(Vertex **start, Vertex **next, bool loop) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<Vector, Vertex *, VectorHash, VectorPred> vertices;
|
||||||
|
std::unordered_map<std::pair<Vertex *, Vertex *>, Edge *, VertexPairHash> edgeMap;
|
||||||
|
std::vector<Edge *> edges;
|
||||||
|
|
||||||
|
~PolylineBuilder();
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
Vertex *AddVertex(const Vector &pos);
|
||||||
|
Edge *AddEdge(const Vector &p0, const Vector &p1, uint32_t kind, uintptr_t data = 0);
|
||||||
|
void Generate(
|
||||||
|
std::function<void(Vertex *start, Vertex *next, Edge *edge)> startFunc,
|
||||||
|
std::function<void(Vertex *next, Edge *edge)> nextFunc,
|
||||||
|
std::function<void(Edge *)> aloneFunc,
|
||||||
|
std::function<void()> endFunc = [](){});
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
189
src/polyline.cpp
Normal file
189
src/polyline.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
19
src/util.cpp
19
src/util.cpp
|
@ -935,6 +935,25 @@ Vector Vector::AtIntersectionOfPlanes(Vector na, double da,
|
||||||
return Vector::From(detx/det, dety/det, detz/det);
|
return Vector::From(detx/det, dety/det, detz/det);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t VectorHash::operator()(const Vector &v) const {
|
||||||
|
const size_t size = (size_t)pow(std::numeric_limits<size_t>::max(), 1.0 / 3.0) - 1;
|
||||||
|
const double eps = 4.0 * LENGTH_EPS;
|
||||||
|
|
||||||
|
double x = fabs(v.x) / eps;
|
||||||
|
double y = fabs(v.y) / eps;
|
||||||
|
double z = fabs(v.y) / eps;
|
||||||
|
|
||||||
|
size_t xs = size_t(fmod(x, (double)size));
|
||||||
|
size_t ys = size_t(fmod(y, (double)size));
|
||||||
|
size_t zs = size_t(fmod(z, (double)size));
|
||||||
|
|
||||||
|
return (zs * size + ys) * size + xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorPred::operator()(Vector a, Vector b) const {
|
||||||
|
return a.Equals(b, LENGTH_EPS);
|
||||||
|
}
|
||||||
|
|
||||||
Vector4 Vector4::From(double w, double x, double y, double z) {
|
Vector4 Vector4::From(double w, double x, double y, double z) {
|
||||||
Vector4 ret;
|
Vector4 ret;
|
||||||
ret.w = w;
|
ret.w = w;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user