Added colorColinear and resetColor algorithms

This commit is contained in:
Markus Lampert
2020-09-07 18:12:26 -07:00
committed by sliptonic
parent f860339658
commit 952c94cb50
9 changed files with 258 additions and 51 deletions

View File

@@ -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);
}
}
}

View File

@@ -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<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;
@@ -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) {

View File

@@ -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>

View File

@@ -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"
@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -73,5 +73,15 @@
<UserDocu>assign given color to all twins of edges (which one is considered a twin is arbitrary)</UserDocu>
</Documentation>
</Methode>
<Methode Name="colorColinear">
<Documentation>
<UserDocu>assign given color to all edges sourced by two segments almost in line with each other (optional angle in degrees)</UserDocu>
</Documentation>
</Methode>
<Methode Name="resetColor">
<Documentation>
<UserDocu>assign color 0 to all elements with the given color</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -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, &degree)) {
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

View File

@@ -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);
}