Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added straight skeleton 2 #29

Merged
merged 14 commits into from
May 12, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton`.

### Changed

### Removed
Binary file added docs/_images/cgal_straight_skeleton_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ API Reference
api/compas_cgal.reconstruction
api/compas_cgal.slicer
api/compas_cgal.skeletonization
api/compas_cgal.straight_skeleton_2
api/compas_cgal.subdivision
api/compas_cgal.triangulation
api/compas_cgal.types
11 changes: 11 additions & 0 deletions docs/api/compas_cgal.straight_skeleton_2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
********************************************************************************
compas_cgal.straight_skeleton_2
********************************************************************************

.. currentmodule:: compas_cgal.straight_skeleton_2

.. autosummary::
:toctree: generated/
:nosignatures:

create_interior_straight_skeleton
31 changes: 31 additions & 0 deletions docs/examples/straight_skeleton_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from compas.datastructures import Graph
from compas.geometry import Polygon
from compas_viewer import Viewer

from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton

points = [
(-1.91, 3.59, 0.0),
(-5.53, -5.22, 0.0),
(-0.39, -1.98, 0.0),
(2.98, -5.51, 0.0),
(4.83, -2.02, 0.0),
(9.70, -3.63, 0.0),
(12.23, 1.25, 0.0),
(3.42, 0.66, 0.0),
(2.92, 4.03, 0.0),
(-1.91, 3.59, 0.0),
]
polygon = Polygon(points)
lines = create_interior_straight_skeleton(points)
graph = Graph.from_lines(lines)

# ==============================================================================
# Viz
# ==============================================================================

viewer = Viewer(width=1600, height=900)
viewer.renderer_config.show_grid = False
viewer.scene.add(graph, edgecolor=(1.0, 0.0, 0.0))
viewer.scene.add(polygon)
viewer.show()
11 changes: 11 additions & 0 deletions docs/examples/straight_skeleton_2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
********************************************************************************
2D Straight Skeleton
********************************************************************************

.. figure:: /_images/cgal_straight_skeleton_2.png
:figclass: figure
:class: figure-img img-fluid


.. literalinclude:: straight_skeleton_2.py
:language: python
2 changes: 2 additions & 0 deletions src/compas_cgal.cpp
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ void init_subdivision(pybind11::module &);
void init_skeletonization(pybind11::module &);
void init_reconstruction(pybind11::module &);
void init_polygonal_surface_reconstruction(pybind11::module &);
void init_straight_skeleton_2(pybind11::module &);

// the first parameter here ("_cgal") will be the name of the "so" or "pyd" file that will be produced by PyBind11
// which is the entry point from where all other modules will be accessible.
@@ -30,4 +31,5 @@ PYBIND11_MODULE(_cgal, m)
init_skeletonization(m);
init_reconstruction(m);
init_polygonal_surface_reconstruction(m);
init_straight_skeleton_2(m);
}
1 change: 1 addition & 0 deletions src/compas_cgal/__init__.py
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
"compas_cgal.measure",
"compas_cgal.slicer",
"compas_cgal.triangulation",
"compas_cgal.straight_skeleton_2",
]

__all__ = ["HOME", "DATA", "DOCS", "TEMP"]
33 changes: 33 additions & 0 deletions src/compas_cgal/straight_skeleton_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import numpy as np
from compas.geometry import normal_polygon
from compas.tolerance import Tolerance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently you should do from compas.tolerance import TOL, which is the global instance of the Tolerance class.

i will replace this with a singleton in the future, but currently this would be better...


from compas_cgal._cgal import straight_skeleton_2

from .types import PolylinesNumpy


def create_interior_straight_skeleton(points) -> PolylinesNumpy:
"""Compute the skeleton of a polygon.

Parameters
----------
points : list of point coordinates or :class:`compas.geometry.Polygon`
The points of the polygon.

Returns
-------
:attr:`compas_cgal.types.PolylinesNumpy`
The skeleton of the polygon.

Raises
------
ValueError
If the normal of the polygon is not [0, 0, 1].
"""
points = list(points)
tol = Tolerance()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

if not tol.is_allclose(normal_polygon(points, True), [0, 0, 1]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TOL.is_allclose

raise ValueError("Please pass a polygon with a normal vector of [0, 0, 1].")
V = np.asarray(points, dtype=np.float64)
return straight_skeleton_2.create_interior_straight_skeleton(V)
56 changes: 56 additions & 0 deletions src/straight_skeleton_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

#include "straight_skeleton_2.h"
#include <CGAL/Polygon_2.h>
#include <CGAL/create_straight_skeleton_2.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point;
typedef CGAL::Polygon_2<K> Polygon_2;
typedef CGAL::Straight_skeleton_2<K> Ss;
typedef boost::shared_ptr<Ss> SsPtr;
typedef CGAL::Straight_skeleton_2<K>::Halfedge_const_handle Halfedge_const_handle;
typedef CGAL::Straight_skeleton_2<K>::Vertex_const_handle Vertex_const_handle;

Edges pmp_create_interior_straight_skeleton(
Eigen::Ref<const compas::RowMatrixXd> &V)
{
Polygon_2 poly;
for (int i = 0; i < V.rows(); i++)
{
poly.push_back(Point(V(i, 0), V(i, 1)));
}
assert(poly.is_counterclockwise_oriented());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps it is worth checking what happens with these assertions on the Python side

Copy link
Member Author

@romanarust romanarust May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i removed the assert, as the orientation check is anyway on python side

SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());
Edges edgelist;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps this typedef conflicts with the one from skeletonization.h

for(auto hit = iss->halfedges_begin(); hit != iss->halfedges_end(); ++hit){
const Halfedge_const_handle h = hit;
if(!h->is_bisector()){
continue;
}
const Vertex_const_handle& v1 = h->vertex();
const Vertex_const_handle& v2 = h->opposite()->vertex();
if(&*v1 < &*v2){
std::vector<double> s_vec = {v1->point().x(), v1->point().y(), 0};
std::vector<double> t_vec = {v2->point().x(), v2->point().y(), 0};
Edge edge = std::make_tuple(s_vec, t_vec);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here...

edgelist.push_back(edge);
}

}
return edgelist;
};


// ===========================================================================
// PyBind11
// ===========================================================================

void init_straight_skeleton_2(pybind11::module &m)
{
pybind11::module submodule = m.def_submodule("straight_skeleton_2");

submodule.def(
"create_interior_straight_skeleton",
&pmp_create_interior_straight_skeleton,
pybind11::arg("V").noconvert());
};
12 changes: 12 additions & 0 deletions src/straight_skeleton_2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef COMPAS_STRAIGHT_SKELETON_2_H
#define COMPAS_STRAIGHT_SKELETON_2_H

#include <compas.h>

typedef std::tuple<std::vector<double>, std::vector<double>> Edge;
typedef std::list<Edge> Edges;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps we can move these guys to the compas namespace?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for the definitions in skeletonization.h...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i put the typedefs of Edge and Edges into the compas namespace and removed them from skeletonization and straight_skeleton_2.. still failing on windows 😒

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It finally builds! just forgot to add the "straight_skeleton_2.cpp" to the setup.py 😅


Edges pmp_create_interior_straight_skeleton(
Eigen::Ref<const compas::RowMatrixXd> &V);

#endif /* COMPAS_STRAIGHT_SKELETON_2_H */
16 changes: 16 additions & 0 deletions tests/test_straight_skeleton_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps it would be worth adding a few tests of "known"/"predictable" skeletons?



def test_straight_polygon():
points = [
(-1, -1, 0),
(0, -12, 0),
(1, -1, 0),
(12, 0, 0),
(1, 1, 0),
(0, 12, 0),
(-1, 1, 0),
(-12, 0, 0),
]
lines = create_interior_straight_skeleton(points)
assert len(lines) == 8