diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb20397..3ac6259 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -143,6 +143,7 @@ set(solvespace_cad_SOURCES mesh.cpp modify.cpp mouse.cpp + polyline.cpp polygon.cpp resource.cpp request.cpp diff --git a/src/dsc.h b/src/dsc.h index 3a895b4..7769621 100644 --- a/src/dsc.h +++ b/src/dsc.h @@ -107,6 +107,14 @@ public: Vector4 Project4d() const; }; +struct VectorHash { + size_t operator()(const Vector &v) const; +}; + +struct VectorPred { + bool operator()(Vector a, Vector b) const; +}; + class Vector4 { public: double w, x, y, z; diff --git a/src/exportvector.cpp b/src/exportvector.cpp index 4dcf685..8732433 100644 --- a/src/exportvector.cpp +++ b/src/exportvector.cpp @@ -6,134 +6,6 @@ #include #include "solvespace.h" -class PolylineBuilder { -public: - struct Edge; - - struct Vertex { - Vector pos; - std::vector 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::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 vertices; - std::vector 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 //----------------------------------------------------------------------------- @@ -245,43 +117,36 @@ public: for(DxfFileWriter::BezierPath &path : writer->paths) { for(SBezier *sb : path.beziers) { 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; - bool loop = false; - do { - found = false; - for(PolylineBuilder::Edge *e : builder.edges) { - if(e->tag != 0) continue; + DRW_LWPolyline polyline; - PolylineBuilder::Vertex *start; - PolylineBuilder::Vertex *next; - if(!e->getStartAndNext(&start, &next, loop)) continue; - found = true; - e->tag = 1; + auto startFunc = [&](PolylineBuilder::Vertex *start, + PolylineBuilder::Vertex *next, + PolylineBuilder::Edge *e) { + hStyle hs = { e->kind }; + 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; - assignEntityDefaults(&polyline, e->style); - 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); - } + auto nextFunc = [&](PolylineBuilder::Vertex *next, PolylineBuilder::Edge *e) { + polyline.vertlist.push_back(new DRW_Vertex2D(next->pos.x, next->pos.y, 0.0)); + }; - if(!found && !loop) { - loop = true; - found = true; - } - } while(found); + auto endFunc = [&]() { + dxf->writeLWPolyline(&polyline); + }; - for(PolylineBuilder::Edge *e : builder.edges) { - if(e->tag != 0) continue; - writeLine(e->a->pos, e->b->pos, e->style); - } + auto aloneFunc = [&](PolylineBuilder::Edge *e) { + hStyle hs = { e->kind }; + writeLine(e->a->pos, e->b->pos, hs); + }; + + builder.Generate(startFunc, nextFunc, aloneFunc, endFunc); } void writeEntities() override { diff --git a/src/polygon.h b/src/polygon.h index bc3e085..2ef818c 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -349,5 +349,54 @@ public: void SnapToVertex(Vector v, SMesh *extras); }; +class PolylineBuilder { +public: + struct Edge; + + struct Vertex { + Vector pos; + std::vector 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 &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 vertices; + std::unordered_map, Edge *, VertexPairHash> edgeMap; + std::vector 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 startFunc, + std::function nextFunc, + std::function aloneFunc, + std::function endFunc = [](){}); +}; + #endif diff --git a/src/polyline.cpp b/src/polyline.cpp new file mode 100644 index 0000000..f7dcace --- /dev/null +++ b/src/polyline.cpp @@ -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 &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 startFunc, + std::function nextFunc, + std::function aloneFunc, + std::function 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); + } +} diff --git a/src/util.cpp b/src/util.cpp index 3a32ab9..f22fc1b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -935,6 +935,25 @@ Vector Vector::AtIntersectionOfPlanes(Vector na, double da, 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::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 ret; ret.w = w;