Added support for distances of edge end points.

This commit is contained in:
Markus Lampert
2020-09-04 19:08:38 -07:00
committed by sliptonic
parent 926d254849
commit 38ff7fcdae
2 changed files with 104 additions and 30 deletions

View File

@@ -96,7 +96,12 @@
</Methode>
<Methode Name="toGeom" Const="true">
<Documentation>
<UserDocu>Returns true if edge goes through endpoint of the segment site</UserDocu>
<UserDocu>Returns a geom representation of the edge (line segment or arc of parabola)</UserDocu>
</Documentation>
</Methode>
<Methode Name="getDistances" Const="true">
<Documentation>
<UserDocu>Returns the distance of the vertices to the input source</UserDocu>
</Documentation>
</Methode>
</PythonExport>

View File

@@ -269,6 +269,36 @@ namespace {
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;
{
offset.x(low(segment).x());
offset.y(low(segment).y());
}
Voronoi::point_type s;
{
s.x(high(segment).x() - offset.x());
s.y(high(segment).y() - offset.y());
}
// move point accordingly so it maintains it's relation to s (p)
Voronoi::point_type p;
{
p.x(point.x() - offset.x());
p.y(point.y() - offset.y());
}
// calculate the orthogonal projection of p onto s
// ((p dot s) / (s dot s)) * s (https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line)
// and it back by original offset to get the projected point
double proj = (p.x() * s.x() + p.y() * s.y()) / (s.x() * s.x() + s.y() * s.y());
Voronoi::point_type pt;
{
pt.x(offset.x() + proj * s.x());
pt.y(offset.y() + proj * s.y());
}
return pt;
}
}
PyObject* VoronoiEdgePy::toGeom(PyObject *args)
@@ -314,7 +344,7 @@ PyObject* VoronoiEdgePy::toGeom(PyObject *args)
direction.y(dx);
}
}
double k = 10.0;
double k = 10.0; // <-- need something smarter here
Voronoi::point_type begin;
Voronoi::point_type end;
if (e->ptr->vertex0()) {
@@ -343,35 +373,10 @@ PyObject* VoronoiEdgePy::toGeom(PyObject *args)
// this is only the mid point of the segment if the parabola is symmetric
Voronoi::point_type loc;
{
// move segment so it goes through the origin (s)
Voronoi::point_type offset;
{
offset.x(low(segment).x());
offset.y(low(segment).y());
}
Voronoi::point_type s;
{
s.x(high(segment).x() - offset.x());
s.y(high(segment).y() - offset.y());
}
// move point accordingly so it maintains it's relation to s (p)
Voronoi::point_type p;
{
p.x(point.x() - offset.x());
p.y(point.y() - offset.y());
}
// calculate the orthogonal projection of p onto s
// ((p dot s) / (s dot s)) * s (https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line)
double proj = (p.x() * s.x() + p.y() * s.y()) / (s.x() * s.x() + s.y() * s.y());
Voronoi::point_type p1;
{
p1.x(proj * s.x());
p1.y(proj * s.y());
}
// finally ...
Voronoi::point_type proj = orthognalProjection(point, segment);
// the location is the mid point between the projection on the segment and the point
loc.x(((offset.x() + p1.x()) + point.x()) / 2);
loc.y(((offset.y() + p1.y()) + point.y()) / 2);
loc.x((proj.x() + point.x()) / 2);
loc.y((proj.y() + point.y()) / 2);
}
Voronoi::point_type axis;
{
@@ -409,6 +414,70 @@ PyObject* VoronoiEdgePy::toGeom(PyObject *args)
return Py_None;
}
namespace {
double distanceBetween(const Voronoi::diagram_type::vertex_type &v0, const Voronoi::point_type &p1) {
double x = v0.x() - p1.x();
double y = v0.y() - p1.y();
return sqrt(x * x + y * y);
}
void addDistanceBetween(const Voronoi::diagram_type::vertex_type *v0, const Voronoi::point_type &p1, Py::List *list) {
if (v0) {
list->append(Py::Float(distanceBetween(*v0, p1)));
} else {
Py_INCREF(Py_None);
list->append(Py::asObject(Py_None));
}
}
void addProjectedDistanceBetween(const Voronoi::diagram_type::vertex_type *v0, const Voronoi::segment_type &segment, Py::List *list) {
if (v0) {
Voronoi::point_type p0;
{
p0.x(v0->x());
p0.y(v0->y());
}
Voronoi::point_type p1 = orthognalProjection(p0, segment);
list->append(Py::Float(distanceBetween(*v0, p1)));
} else {
Py_INCREF(Py_None);
list->append(Py::asObject(Py_None));
}
}
bool addDistancesToPoint(const VoronoiEdge *edge, Voronoi::point_type p, Py::List *list) {
addDistanceBetween(edge->ptr->vertex0(), p, list);
addDistanceBetween(edge->ptr->vertex1(), p, list);
return true;
}
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);
}
const Voronoi::diagram_type::cell_type *c1 = edge->ptr->twin()->cell();
if (c1->contains_point()) {
return addDistancesToPoint(edge, retrievePoint(edge->dia, c1), list);
}
// 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);
addProjectedDistanceBetween(edge->ptr->vertex0(), segment, list);
addProjectedDistanceBetween(edge->ptr->vertex1(), segment, list);
return false;
}
}
PyObject* VoronoiEdgePy::getDistances(PyObject *args)
{
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
Py::List list;
retrieveDistances(e, &list);
return Py::new_reference_to(list);
}
// custom attributes get/set
PyObject* VoronoiEdgePy::getCustomAttributes(const char* /*attr*/) const