From 952c94cb50ef09e29cd722906318af5a0af1d86f Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 7 Sep 2020 18:12:26 -0700 Subject: [PATCH] Added colorColinear and resetColor algorithms --- src/Mod/Path/App/Voronoi.cpp | 106 +++++++++++++++++++++++- src/Mod/Path/App/Voronoi.h | 18 +++- src/Mod/Path/App/VoronoiCellPy.xml | 5 ++ src/Mod/Path/App/VoronoiCellPyImp.cpp | 30 +++++-- src/Mod/Path/App/VoronoiEdgePy.xml | 5 ++ src/Mod/Path/App/VoronoiEdgePyImp.cpp | 95 ++++++++++++++------- src/Mod/Path/App/VoronoiPy.xml | 10 +++ src/Mod/Path/App/VoronoiPyImp.cpp | 32 ++++++- src/Mod/Path/App/VoronoiVertexPyImp.cpp | 8 +- 9 files changed, 258 insertions(+), 51 deletions(-) diff --git a/src/Mod/Path/App/Voronoi.cpp b/src/Mod/Path/App/Voronoi.cpp index ddde461c7d..abd4ca6450 100644 --- a/src/Mod/Path/App/Voronoi.cpp +++ b/src/Mod/Path/App/Voronoi.cpp @@ -132,6 +132,26 @@ void Voronoi::diagram_type::reIndex() { } } +Voronoi::point_type Voronoi::diagram_type::retrievePoint(const Voronoi::diagram_type::cell_type *cell) const { + Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index(); + Voronoi::diagram_type::cell_type::source_category_type category = cell->source_category(); + if (category == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) { + return points[index]; + } + index -= points.size(); + if (category == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) { + return low(segments[index]); + } else { + return high(segments[index]); + } +} + +Voronoi::segment_type Voronoi::diagram_type::retrieveSegment(const Voronoi::diagram_type::cell_type *cell) const { + Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index() - points.size(); + return segments[index]; +} + + // Voronoi Voronoi::Voronoi() @@ -188,7 +208,7 @@ void Voronoi::construct() vd->reIndex(); } -void Voronoi::colorExterior(int color) { +void Voronoi::colorExterior(Voronoi::color_type color) { for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) { if (!it->is_finite()) { ::colorExterior(&(*it), color); @@ -196,7 +216,7 @@ void Voronoi::colorExterior(int color) { } } -void Voronoi::colorTwins(int color) { +void Voronoi::colorTwins(Voronoi::color_type color) { for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) { if (!it->color()) { auto twin = it->twin(); @@ -206,3 +226,85 @@ void Voronoi::colorTwins(int color) { } } } + +double Voronoi::diagram_type::angleOfSegment(int i, Voronoi::diagram_type::angle_map_t *angle) const { + Voronoi::diagram_type::angle_map_t::const_iterator a = angle ? angle->find(i) : Voronoi::diagram_type::angle_map_t::const_iterator(); + if (!angle || a == angle->end()) { + Voronoi::point_type p0 = low(segments[i]); + Voronoi::point_type p1 = high(segments[i]); + double ang = 0; + if (p0.x() == p1.x()) { + if ((p0.y() > 0 && p1.y() > 0) || (p0.y() > 0 && p1.y() > 0)) { + ang = M_PI_2; + } else { + ang = -M_PI_2; + } + } else { + ang = atan((p0.y() - p1.y()) / (p0.x() - p1.x())); + } + if (angle) { + angle->insert(angle_map_t::value_type(i, ang)); + } + return ang; + } + return a->second; +} + +static bool pointsMatch(const Voronoi::point_type &p0, const Voronoi::point_type &p1) { + return long(p0.x()) == long(p1.x()) && long(p0.y()) == long(p1.y()); +} + +bool Voronoi::diagram_type::segmentsAreConnected(int i, int j) const { + return + pointsMatch(low(segments[i]), low(segments[j])) + || pointsMatch(low(segments[i]), high(segments[j])) + || pointsMatch(high(segments[i]), low(segments[j])) + || pointsMatch(high(segments[i]), high(segments[j])); +} + +void Voronoi::colorColinear(Voronoi::color_type color, double degree) { + double rad = degree * M_PI / 180; + + Voronoi::diagram_type::angle_map_t angle; + int psize = vd->points.size(); + + for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) { + int i0 = it->cell()->source_index() - psize; + int i1 = it->twin()->cell()->source_index() - psize; + if (it->color() == 0 + && it->cell()->contains_segment() + && it->twin()->cell()->contains_segment() + && vd->segmentsAreConnected(i0, i1)) { + double a0 = vd->angleOfSegment(i0, &angle); + double a1 = vd->angleOfSegment(i1, &angle); + double a = a0 - a1; + if (a > M_PI_2) { + a -= M_PI; + } else if (a < -M_PI_2) { + a += M_PI; + } + if (fabs(a) < rad) { + it->color(color); + it->twin()->color(color); + } + } + } +} + +void Voronoi::resetColor(Voronoi::color_type color) { + for (auto it = vd->cells().begin(); it != vd->cells().end(); ++it) { + if (it->color() == color) { + it->color(0); + } + } + for (auto it = vd->edges().begin(); it != vd->edges().end(); ++it) { + if (it->color() == color) { + it->color(0); + } + } + for (auto it = vd->vertices().begin(); it != vd->vertices().end(); ++it) { + if (it->color() == color) { + it->color(0); + } + } +} diff --git a/src/Mod/Path/App/Voronoi.h b/src/Mod/Path/App/Voronoi.h index 2aa786b058..272a81321b 100644 --- a/src/Mod/Path/App/Voronoi.h +++ b/src/Mod/Path/App/Voronoi.h @@ -46,8 +46,9 @@ namespace Path Voronoi(); ~Voronoi(); - static const int InvalidIndex = INT_MAX; - static const int ColorMask = 0x07FFFFFF; // top 5 bits reserved internally + typedef std::size_t color_type; + static const int InvalidIndex = INT_MAX; + static const color_type ColorMask = 0x07FFFFFFFFFFFFFFul; // top 5 bits reserved internally // types typedef double coordinate_type; @@ -82,6 +83,13 @@ namespace Path std::vector points; std::vector segments; + point_type retrievePoint(const cell_type *cell) const; + segment_type retrieveSegment(const cell_type *cell) const; + + typedef std::map angle_map_t; + double angleOfSegment(int i, angle_map_t *angle = 0) const; + bool segmentsAreConnected(int i, int j) const; + private: double scale; cell_map_type cell_index; @@ -99,8 +107,10 @@ namespace Path long numEdges() const; long numVertices() const; - void colorExterior(int color); - void colorTwins(int color); + void resetColor(color_type color); + void colorExterior(color_type color); + void colorTwins(color_type color); + void colorColinear(color_type color, double degree); template T* create(int index) { diff --git a/src/Mod/Path/App/VoronoiCellPy.xml b/src/Mod/Path/App/VoronoiCellPy.xml index 7da58f3fdd..48601d0847 100644 --- a/src/Mod/Path/App/VoronoiCellPy.xml +++ b/src/Mod/Path/App/VoronoiCellPy.xml @@ -55,5 +55,10 @@ Returns true if the cell doesn't have an incident edge + + + Returns the Source for the cell + + diff --git a/src/Mod/Path/App/VoronoiCellPyImp.cpp b/src/Mod/Path/App/VoronoiCellPyImp.cpp index bf7e9e9353..9a74a18447 100644 --- a/src/Mod/Path/App/VoronoiCellPyImp.cpp +++ b/src/Mod/Path/App/VoronoiCellPyImp.cpp @@ -27,7 +27,6 @@ # include #endif -#include "Mod/Path/App/Voronoi.h" #include "Mod/Path/App/Voronoi.h" #include "Mod/Path/App/VoronoiCell.h" #include "Mod/Path/App/VoronoiCellPy.h" @@ -112,15 +111,15 @@ VoronoiCell* getVoronoiCellFromPy(const VoronoiCellPy *c, PyObject *args = 0) { return self; } -Py::Int VoronoiCellPy::getColor(void) const { +Py::Long VoronoiCellPy::getColor(void) const { VoronoiCell *c = getVoronoiCellPtr(); if (c->isBound()) { - return Py::Int(c->ptr->color() & Voronoi::ColorMask); + return Py::Long(c->ptr->color() & Voronoi::ColorMask); } - return Py::Int(0); + return Py::Long(0); } -void VoronoiCellPy::setColor(Py::Int color) { +void VoronoiCellPy::setColor(Py::Long color) { getCellFromPy(this)->color(int(color) & Voronoi::ColorMask); } @@ -166,6 +165,27 @@ PyObject* VoronoiCellPy::isDegenerate(PyObject *args) return chk; } +PyObject* VoronoiCellPy::getSource(PyObject *args) +{ + double z = 0; + if (!PyArg_ParseTuple(args, "|d", &z)) { + throw Py::TypeError("Optional z argument (double) accepted"); + } + + VoronoiCell *c = getVoronoiCellFromPy(this); + if (c->ptr->contains_point()) { + Base::Vector3d v = c->dia->scaledVector(c->dia->retrievePoint(c->ptr), z); + return new Base::VectorPy(new Base::Vector3d(v)); + } + Voronoi::segment_type s = c->dia->retrieveSegment(c->ptr); + Base::Vector3d v0 = c->dia->scaledVector(low(s), z); + Base::Vector3d v1 = c->dia->scaledVector(high(s), z); + Py::List list; + list.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(v0)))); + list.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(v1)))); + return Py::new_reference_to(list); +} + // custom attributes get/set diff --git a/src/Mod/Path/App/VoronoiEdgePy.xml b/src/Mod/Path/App/VoronoiEdgePy.xml index c05f273e52..ddefc4586f 100644 --- a/src/Mod/Path/App/VoronoiEdgePy.xml +++ b/src/Mod/Path/App/VoronoiEdgePy.xml @@ -104,5 +104,10 @@ Returns the distance of the vertices to the input source + + + Returns the angle (in degree) of the segments if the edge was formed by two segments + + diff --git a/src/Mod/Path/App/VoronoiEdgePyImp.cpp b/src/Mod/Path/App/VoronoiEdgePyImp.cpp index 4b3a2449a1..0c6fb6c688 100644 --- a/src/Mod/Path/App/VoronoiEdgePyImp.cpp +++ b/src/Mod/Path/App/VoronoiEdgePyImp.cpp @@ -130,15 +130,15 @@ VoronoiEdge* getVoronoiEdgeFromPy(const VoronoiEdgePy *e, PyObject *args = 0) { return self; } -Py::Int VoronoiEdgePy::getColor(void) const { +Py::Long VoronoiEdgePy::getColor(void) const { VoronoiEdge *e = getVoronoiEdgePtr(); if (e->isBound()) { - return Py::Int(e->ptr->color() & Voronoi::ColorMask); + return Py::Long(e->ptr->color() & Voronoi::ColorMask); } - return Py::Int(0); + return Py::Long(0); } -void VoronoiEdgePy::setColor(Py::Int color) { +void VoronoiEdgePy::setColor(Py::Long color) { getEdgeFromPy(this)->color(int(color) & Voronoi::ColorMask); } @@ -251,25 +251,6 @@ PyObject* VoronoiEdgePy::isSecondary(PyObject *args) } namespace { - Voronoi::point_type retrievePoint(Voronoi::diagram_type *dia, const Voronoi::diagram_type::cell_type *cell) { - Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index(); - Voronoi::diagram_type::cell_type::source_category_type category = cell->source_category(); - if (category == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) { - return dia->points[index]; - } - index -= dia->points.size(); - if (category == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) { - return low(dia->segments[index]); - } else { - return high(dia->segments[index]); - } - } - - Voronoi::segment_type retrieveSegment(Voronoi::diagram_type *dia, const Voronoi::diagram_type::cell_type *cell) { - Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index() - dia->points.size(); - return dia->segments[index]; - } - Voronoi::point_type orthognalProjection(const Voronoi::point_type &point, const Voronoi::segment_type &segment) { // move segment so it goes through the origin (s) Voronoi::point_type offset; @@ -325,15 +306,15 @@ PyObject* VoronoiEdgePy::toGeom(PyObject *args) Voronoi::point_type origin; Voronoi::point_type direction; if (c0->contains_point() && c1->contains_point()) { - Voronoi::point_type p0 = retrievePoint(e->dia, c0); - Voronoi::point_type p1 = retrievePoint(e->dia, c1); + Voronoi::point_type p0 = e->dia->retrievePoint(c0); + Voronoi::point_type p1 = e->dia->retrievePoint(c1); origin.x((p0.x() + p1.x()) / 2.); origin.y((p0.y() + p1.y()) / 2.); direction.x(p0.y() - p1.y()); direction.y(p1.x() - p0.x()); } else { - origin = c0->contains_segment() ? retrievePoint(e->dia, c1) : retrievePoint(e->dia, c0); - Voronoi::segment_type segment = c0->contains_segment() ? retrieveSegment(e->dia, c0) : retrieveSegment(e->dia, c1); + origin = c0->contains_segment() ? e->dia->retrievePoint(c1) : e->dia->retrievePoint(c0); + Voronoi::segment_type segment = c0->contains_segment() ? e->dia->retrieveSegment(c0) : e->dia->retrieveSegment(c1); Voronoi::coordinate_type dx = high(segment).x() - low(segment).x(); Voronoi::coordinate_type dy = high(segment).y() - low(segment).y(); if ((low(segment) == origin) ^ c0->contains_point()) { @@ -367,8 +348,8 @@ PyObject* VoronoiEdgePy::toGeom(PyObject *args) } } else { // parabolic curve, which is always formed by a point and an edge - Voronoi::point_type point = e->ptr->cell()->contains_point() ? retrievePoint(e->dia, e->ptr->cell()) : retrievePoint(e->dia, e->ptr->twin()->cell()); - Voronoi::segment_type segment = e->ptr->cell()->contains_point() ? retrieveSegment(e->dia, e->ptr->twin()->cell()) : retrieveSegment(e->dia, e->ptr->cell()); + Voronoi::point_type point = e->ptr->cell()->contains_point() ? e->dia->retrievePoint(e->ptr->cell()) : e->dia->retrievePoint(e->ptr->twin()->cell()); + Voronoi::segment_type segment = e->ptr->cell()->contains_point() ? e->dia->retrieveSegment(e->ptr->twin()->cell()) : e->dia->retrieveSegment(e->ptr->cell()); // the location is the mid point betwenn the normal on the segment through point // this is only the mid point of the segment if the parabola is symmetric Voronoi::point_type loc; @@ -456,14 +437,14 @@ namespace { bool retrieveDistances(const VoronoiEdge *edge, Py::List *list) { const Voronoi::diagram_type::cell_type *c0 = edge->ptr->cell(); if (c0->contains_point()) { - return addDistancesToPoint(edge, retrievePoint(edge->dia, c0), list, edge->dia->getScale()); + return addDistancesToPoint(edge, edge->dia->retrievePoint(c0), list, edge->dia->getScale()); } const Voronoi::diagram_type::cell_type *c1 = edge->ptr->twin()->cell(); if (c1->contains_point()) { - return addDistancesToPoint(edge, retrievePoint(edge->dia, c1), list, edge->dia->getScale()); + return addDistancesToPoint(edge, edge->dia->retrievePoint(c1), list, edge->dia->getScale()); } // at this point both cells are sourced from segments and it does not matter which one we use - Voronoi::segment_type segment = retrieveSegment(edge->dia, c0); + Voronoi::segment_type segment = edge->dia->retrieveSegment(c0); addProjectedDistanceBetween(edge->ptr->vertex0(), segment, list, edge->dia->getScale()); addProjectedDistanceBetween(edge->ptr->vertex1(), segment, list, edge->dia->getScale()); return false; @@ -478,6 +459,56 @@ PyObject* VoronoiEdgePy::getDistances(PyObject *args) return Py::new_reference_to(list); } +std::ostream& operator<<(std::ostream &str, const Voronoi::point_type &p) { + return str << "[" << int(p.x()) << ", " << int(p.y()) << "]"; +} + +std::ostream& operator<<(std::ostream &str, const Voronoi::segment_type &s) { + return str << '<' << low(s) << '-' << high(s) << '>'; +} + +static bool pointsMatch(const Voronoi::point_type &p0, const Voronoi::point_type &p1) { + return long(p0.x()) == long(p1.x()) && long(p0.y()) == long(p1.y()); +} + +static void printCompare(const char *label, const Voronoi::point_type &p0, const Voronoi::point_type &p1) { + std::cerr << " " << label <<": " << pointsMatch(p1, p0) << pointsMatch(p0, p1) << " " << p0 << ' ' << p1 << std::endl; +} + +PyObject* VoronoiEdgePy::getSegmentAngle(PyObject *args) +{ + VoronoiEdge *e = getVoronoiEdgeFromPy(this, args); + + if (e->ptr->cell()->contains_segment() && e->ptr->twin()->cell()->contains_segment()) { + int i0 = e->ptr->cell()->source_index() - e->dia->points.size(); + int i1 = e->ptr->twin()->cell()->source_index() - e->dia->points.size(); + if (e->dia->segmentsAreConnected(i0, i1)) { + double a0 = e->dia->angleOfSegment(i0); + double a1 = e->dia->angleOfSegment(i1); + double a = a0 - a1; + if (a > M_PI_2) { + a -= M_PI; + } else if (a < -M_PI_2) { + a += M_PI; + } + return Py::new_reference_to(Py::Float(a)); + } else { + std::cerr << "indices: " << std::endl; + std::cerr << " " << e->dia->segments[i0] << std::endl; + std::cerr << " " << e->dia->segments[i1] << std::endl; + std::cerr << " connected: " << e->dia->segmentsAreConnected(i0, i1) << std::endl; + printCompare("l/l", low(e->dia->segments[i0]), low(e->dia->segments[i1])); + printCompare("l/h", low(e->dia->segments[i0]), high(e->dia->segments[i1])); + printCompare("h/l", high(e->dia->segments[i0]), low(e->dia->segments[i1])); + printCompare("h/h", high(e->dia->segments[i0]), high(e->dia->segments[i1])); + } + } else { + std::cerr << "constains_segment(" << e->ptr->cell()->contains_segment() << ", " << e->ptr->twin()->cell()->contains_segment() << ")" << std::endl; + } + Py_INCREF(Py_None); + return Py_None; +} + // custom attributes get/set PyObject* VoronoiEdgePy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/Path/App/VoronoiPy.xml b/src/Mod/Path/App/VoronoiPy.xml index b6227aaf01..30c0576b92 100644 --- a/src/Mod/Path/App/VoronoiPy.xml +++ b/src/Mod/Path/App/VoronoiPy.xml @@ -73,5 +73,15 @@ assign given color to all twins of edges (which one is considered a twin is arbitrary) + + + assign given color to all edges sourced by two segments almost in line with each other (optional angle in degrees) + + + + + assign color 0 to all elements with the given color + + diff --git a/src/Mod/Path/App/VoronoiPyImp.cpp b/src/Mod/Path/App/VoronoiPyImp.cpp index e536232c54..ed3724483f 100644 --- a/src/Mod/Path/App/VoronoiPyImp.cpp +++ b/src/Mod/Path/App/VoronoiPyImp.cpp @@ -173,8 +173,8 @@ Py::List VoronoiPy::getCells(void) const { } PyObject* VoronoiPy::colorExterior(PyObject *args) { - int color = 0; - if (!PyArg_ParseTuple(args, "i", &color)) { + Voronoi::color_type color = 0; + if (!PyArg_ParseTuple(args, "k", &color)) { throw Py::RuntimeError("colorExterior requires an integer (color) argument"); } getVoronoiPtr()->colorExterior(color); @@ -184,8 +184,8 @@ PyObject* VoronoiPy::colorExterior(PyObject *args) { } PyObject* VoronoiPy::colorTwins(PyObject *args) { - int color = 0; - if (!PyArg_ParseTuple(args, "i", &color)) { + Voronoi::color_type color = 0; + if (!PyArg_ParseTuple(args, "k", &color)) { throw Py::RuntimeError("colorTwins requires an integer (color) argument"); } getVoronoiPtr()->colorTwins(color); @@ -194,6 +194,30 @@ PyObject* VoronoiPy::colorTwins(PyObject *args) { return Py_None; } +PyObject* VoronoiPy::colorColinear(PyObject *args) { + Voronoi::color_type color = 0; + double degree = 10.; + if (!PyArg_ParseTuple(args, "k|d", &color, °ree)) { + throw Py::RuntimeError("colorColinear requires an integer (color) and optionally a derivation in degrees argument (default 10)"); + } + getVoronoiPtr()->colorColinear(color, degree); + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* VoronoiPy::resetColor(PyObject *args) { + Voronoi::color_type color = 0; + if (!PyArg_ParseTuple(args, "k", &color)) { + throw Py::RuntimeError("clearColor requires an integer (color) argument"); + } + + getVoronoiPtr()->resetColor(color); + + Py_INCREF(Py_None); + return Py_None; +} + // custom attributes get/set PyObject *VoronoiPy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/Path/App/VoronoiVertexPyImp.cpp b/src/Mod/Path/App/VoronoiVertexPyImp.cpp index edd99a0d50..0a8b7a92ba 100644 --- a/src/Mod/Path/App/VoronoiVertexPyImp.cpp +++ b/src/Mod/Path/App/VoronoiVertexPyImp.cpp @@ -113,15 +113,15 @@ VoronoiVertex* getVoronoiVertexFromPy(const VoronoiVertexPy *v, PyObject *args = } -Py::Int VoronoiVertexPy::getColor(void) const { +Py::Long VoronoiVertexPy::getColor(void) const { VoronoiVertex *v = getVoronoiVertexPtr(); if (v->isBound()) { - return Py::Int(v->ptr->color() & Voronoi::ColorMask); + return Py::Long(v->ptr->color() & Voronoi::ColorMask); } - return Py::Int(0); + return Py::Long(0); } -void VoronoiVertexPy::setColor(Py::Int color) { +void VoronoiVertexPy::setColor(Py::Long color) { getVertexFromPy(this)->color(int(color) & Voronoi::ColorMask); }