Skip to content

Commit

Permalink
Added colorColinear and resetColor algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
mlampert authored and sliptonic committed Sep 28, 2020
1 parent f860339 commit 952c94c
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 51 deletions.
106 changes: 104 additions & 2 deletions src/Mod/Path/App/Voronoi.cpp
Expand Up @@ -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()
Expand Down Expand Up @@ -188,15 +208,15 @@ 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);
}
}
}

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();
Expand All @@ -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);
}
}
}
18 changes: 14 additions & 4 deletions src/Mod/Path/App/Voronoi.h
Expand Up @@ -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;
Expand Down Expand Up @@ -82,6 +83,13 @@ namespace Path
std::vector<point_type> points;
std::vector<segment_type> segments;

point_type retrievePoint(const cell_type *cell) const;
segment_type retrieveSegment(const cell_type *cell) const;

typedef std::map<int, double> 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;
Expand All @@ -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<typename T>
T* create(int index) {
Expand Down
5 changes: 5 additions & 0 deletions src/Mod/Path/App/VoronoiCellPy.xml
Expand Up @@ -55,5 +55,10 @@
<UserDocu>Returns true if the cell doesn't have an incident edge</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSource" Const="true">
<Documentation>
<UserDocu>Returns the Source for the cell</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>
30 changes: 25 additions & 5 deletions src/Mod/Path/App/VoronoiCellPyImp.cpp
Expand Up @@ -27,7 +27,6 @@
# include <boost/algorithm/string.hpp>
#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"
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions src/Mod/Path/App/VoronoiEdgePy.xml
Expand Up @@ -104,5 +104,10 @@
<UserDocu>Returns the distance of the vertices to the input source</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSegmentAngle" Const="true">
<Documentation>
<UserDocu>Returns the angle (in degree) of the segments if the edge was formed by two segments</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>
95 changes: 63 additions & 32 deletions src/Mod/Path/App/VoronoiEdgePyImp.cpp
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down

0 comments on commit 952c94c

Please sign in to comment.