Skip to content

Commit acd1f64

Browse files
authored
Merge pull request #29 from compas-dev/feature/straight_skeleton_2
Added straight skeleton 2
2 parents e91ac21 + c229d1a commit acd1f64

17 files changed

+220
-8
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* Added `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton`.
13+
1214
### Changed
1315

1416
### Removed
54.7 KB
Loading

docs/api.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ API Reference
1212
api/compas_cgal.reconstruction
1313
api/compas_cgal.slicer
1414
api/compas_cgal.skeletonization
15+
api/compas_cgal.straight_skeleton_2
1516
api/compas_cgal.subdivision
1617
api/compas_cgal.triangulation
1718
api/compas_cgal.types
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
********************************************************************************
2+
compas_cgal.straight_skeleton_2
3+
********************************************************************************
4+
5+
.. currentmodule:: compas_cgal.straight_skeleton_2
6+
7+
.. autosummary::
8+
:toctree: generated/
9+
:nosignatures:
10+
11+
create_interior_straight_skeleton

docs/examples/straight_skeleton_2.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from compas.datastructures import Graph
2+
from compas.geometry import Polygon
3+
from compas_viewer import Viewer
4+
5+
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
6+
7+
points = [
8+
(-1.91, 3.59, 0.0),
9+
(-5.53, -5.22, 0.0),
10+
(-0.39, -1.98, 0.0),
11+
(2.98, -5.51, 0.0),
12+
(4.83, -2.02, 0.0),
13+
(9.70, -3.63, 0.0),
14+
(12.23, 1.25, 0.0),
15+
(3.42, 0.66, 0.0),
16+
(2.92, 4.03, 0.0),
17+
(-1.91, 3.59, 0.0),
18+
]
19+
polygon = Polygon(points)
20+
lines = create_interior_straight_skeleton(points)
21+
graph = Graph.from_lines(lines)
22+
23+
# ==============================================================================
24+
# Viz
25+
# ==============================================================================
26+
27+
viewer = Viewer(width=1600, height=900)
28+
viewer.renderer_config.show_grid = False
29+
viewer.scene.add(graph, edgecolor=(1.0, 0.0, 0.0))
30+
viewer.scene.add(polygon)
31+
viewer.show()

docs/examples/straight_skeleton_2.rst

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
********************************************************************************
2+
2D Straight Skeleton
3+
********************************************************************************
4+
5+
.. figure:: /_images/cgal_straight_skeleton_2.png
6+
:figclass: figure
7+
:class: figure-img img-fluid
8+
9+
10+
.. literalinclude:: straight_skeleton_2.py
11+
:language: python

include/compas.h

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ namespace compas
2424
using Mesh = CGAL::Surface_mesh<Point>;
2525
using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
2626
using RowMatrixXi = Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
27+
using Edge = std::tuple<std::vector<double>, std::vector<double>>;
28+
using Edges = std::list<Edge>;
2729

2830
Polyhedron polyhedron_from_vertices_and_faces(const RowMatrixXd &V, const RowMatrixXi &F);
2931

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def get_scip_library():
5252
"src/skeletonization.cpp",
5353
"src/reconstruction.cpp",
5454
"src/polygonal_surface_reconstruction.cpp",
55+
"src/straight_skeleton_2.cpp",
5556
]
5657
),
5758
include_dirs=["./include", get_eigen_include(), get_pybind_include()],

src/compas_cgal.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void init_subdivision(pybind11::module &);
1212
void init_skeletonization(pybind11::module &);
1313
void init_reconstruction(pybind11::module &);
1414
void init_polygonal_surface_reconstruction(pybind11::module &);
15+
void init_straight_skeleton_2(pybind11::module &);
1516

1617
// the first parameter here ("_cgal") will be the name of the "so" or "pyd" file that will be produced by PyBind11
1718
// which is the entry point from where all other modules will be accessible.
@@ -30,4 +31,5 @@ PYBIND11_MODULE(_cgal, m)
3031
init_skeletonization(m);
3132
init_reconstruction(m);
3233
init_polygonal_surface_reconstruction(m);
34+
init_straight_skeleton_2(m);
3335
}

src/compas_cgal/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"compas_cgal.measure",
2323
"compas_cgal.slicer",
2424
"compas_cgal.triangulation",
25+
"compas_cgal.straight_skeleton_2",
2526
]
2627

2728
__all__ = ["HOME", "DATA", "DOCS", "TEMP"]
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import numpy as np
2+
from compas.geometry import normal_polygon
3+
from compas.tolerance import TOL
4+
5+
from compas_cgal._cgal import straight_skeleton_2
6+
7+
from .types import PolylinesNumpy
8+
9+
10+
def create_interior_straight_skeleton(points) -> PolylinesNumpy:
11+
"""Compute the skeleton of a polygon.
12+
13+
Parameters
14+
----------
15+
points : list of point coordinates or :class:`compas.geometry.Polygon`
16+
The points of the polygon.
17+
18+
Returns
19+
-------
20+
:attr:`compas_cgal.types.PolylinesNumpy`
21+
The skeleton of the polygon.
22+
23+
Raises
24+
------
25+
ValueError
26+
If the normal of the polygon is not [0, 0, 1].
27+
"""
28+
points = list(points)
29+
if not TOL.is_allclose(normal_polygon(points, True), [0, 0, 1]):
30+
raise ValueError("Please pass a polygon with a normal vector of [0, 0, 1].")
31+
V = np.asarray(points, dtype=np.float64)
32+
return straight_skeleton_2.create_interior_straight_skeleton(V)

src/skeletonization.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct Split_polylines
3939
}
4040
};
4141

42-
Edges pmp_mesh_skeleton(
42+
compas::Edges pmp_mesh_skeleton(
4343
Eigen::Ref<const compas::RowMatrixXd> &V,
4444
Eigen::Ref<const compas::RowMatrixXi> &F)
4545
{
@@ -63,7 +63,7 @@ Edges pmp_mesh_skeleton(
6363
// Split_polylines splitter(skeleton, polylines);
6464
// CGAL::split_graph_into_polylines(skeleton, splitter);
6565

66-
Edges edgelist;
66+
compas::Edges edgelist;
6767

6868
for (Skeleton_edge e : CGAL::make_range(edges(skeleton)))
6969
{
@@ -72,7 +72,7 @@ Edges pmp_mesh_skeleton(
7272

7373
std::vector<double> s_vec = {s.x(), s.y(), s.z()};
7474
std::vector<double> t_vec = {t.x(), t.y(), t.z()};
75-
Edge edge = std::make_tuple(s_vec, t_vec);
75+
compas::Edge edge = std::make_tuple(s_vec, t_vec);
7676

7777
edgelist.push_back(edge);
7878
}

src/skeletonization.h

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33

44
#include <compas.h>
55

6-
typedef std::tuple<std::vector<double>, std::vector<double>> Edge;
7-
typedef std::list<Edge> Edges;
8-
9-
Edges pmp_mesh_skeleton(
6+
compas::Edges pmp_mesh_skeleton(
107
Eigen::Ref<const compas::RowMatrixXd> &V,
118
Eigen::Ref<const compas::RowMatrixXi> &F);
129

src/straight_skeleton_2.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
#include "straight_skeleton_2.h"
3+
#include <CGAL/Polygon_2.h>
4+
#include <CGAL/create_straight_skeleton_2.h>
5+
6+
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
7+
typedef K::Point_2 Point;
8+
typedef CGAL::Polygon_2<K> Polygon_2;
9+
typedef CGAL::Straight_skeleton_2<K> Ss;
10+
typedef boost::shared_ptr<Ss> SsPtr;
11+
typedef CGAL::Straight_skeleton_2<K>::Halfedge_const_handle Halfedge_const_handle;
12+
typedef CGAL::Straight_skeleton_2<K>::Vertex_const_handle Vertex_const_handle;
13+
14+
compas::Edges pmp_create_interior_straight_skeleton(
15+
Eigen::Ref<const compas::RowMatrixXd> &V)
16+
{
17+
Polygon_2 poly;
18+
for (int i = 0; i < V.rows(); i++)
19+
{
20+
poly.push_back(Point(V(i, 0), V(i, 1)));
21+
}
22+
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());
23+
compas::Edges edgelist;
24+
for(auto hit = iss->halfedges_begin(); hit != iss->halfedges_end(); ++hit){
25+
const Halfedge_const_handle h = hit;
26+
if(!h->is_bisector()){
27+
continue;
28+
}
29+
const Vertex_const_handle& v1 = h->vertex();
30+
const Vertex_const_handle& v2 = h->opposite()->vertex();
31+
if(&*v1 < &*v2){
32+
std::vector<double> s_vec = {v1->point().x(), v1->point().y(), 0};
33+
std::vector<double> t_vec = {v2->point().x(), v2->point().y(), 0};
34+
compas::Edge edge = std::make_tuple(s_vec, t_vec);
35+
edgelist.push_back(edge);
36+
}
37+
38+
}
39+
return edgelist;
40+
};
41+
42+
43+
// ===========================================================================
44+
// PyBind11
45+
// ===========================================================================
46+
47+
void init_straight_skeleton_2(pybind11::module &m)
48+
{
49+
pybind11::module submodule = m.def_submodule("straight_skeleton_2");
50+
51+
submodule.def(
52+
"create_interior_straight_skeleton",
53+
&pmp_create_interior_straight_skeleton,
54+
pybind11::arg("V").noconvert());
55+
};

src/straight_skeleton_2.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef COMPAS_STRAIGHT_SKELETON_2_H
2+
#define COMPAS_STRAIGHT_SKELETON_2_H
3+
4+
#include <compas.h>
5+
6+
7+
compas::Edges pmp_create_interior_straight_skeleton(
8+
Eigen::Ref<const compas::RowMatrixXd> &V);
9+
10+
#endif /* COMPAS_STRAIGHT_SKELETON_2_H */

tests/PLACEHOLDER

-1
This file was deleted.

tests/test_straight_skeleton_2.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
2+
from compas.tolerance import TOL
3+
4+
5+
def test_straight_polygon():
6+
points = [
7+
(-1, -1, 0),
8+
(0, -12, 0),
9+
(1, -1, 0),
10+
(12, 0, 0),
11+
(1, 1, 0),
12+
(0, 12, 0),
13+
(-1, 1, 0),
14+
(-12, 0, 0),
15+
]
16+
lines = create_interior_straight_skeleton(points)
17+
assert len(lines) == 8
18+
19+
20+
def test_straight_polygon_2_compare():
21+
points = [
22+
(-1.91, 3.59, 0.0),
23+
(-5.53, -5.22, 0.0),
24+
(-0.39, -1.98, 0.0),
25+
(2.98, -5.51, 0.0),
26+
(4.83, -2.02, 0.0),
27+
(9.70, -3.63, 0.0),
28+
(12.23, 1.25, 0.0),
29+
(3.42, 0.66, 0.0),
30+
(2.92, 4.03, 0.0),
31+
(-1.91, 3.59, 0.0),
32+
]
33+
lines = create_interior_straight_skeleton(points)
34+
35+
expected = [
36+
[[-1.91, 3.59, 0.0], [-0.139446292, 1.191439787, 0.0]],
37+
[[-5.53, -5.22, 0.0], [-0.139446292, 1.191439787, 0.0]],
38+
[[-0.39, -1.98, 0.0], [0.008499564, 1.241560466, 0.0]],
39+
[[2.98, -5.51, 0.0], [2.44972507, -1.674799065, 0.0]],
40+
[[4.83, -2.02, 0.0], [4.228131167, -0.522007766, 0.0]],
41+
[[8.663865218, -1.084821998, 0.0], [9.7, -3.63, 0.0]],
42+
[[12.23, 1.25, 0.0], [8.663865218, -1.084821998, 0.0]],
43+
[[3.42, 0.66, 0.0], [1.755862468, -1.404991433, 0.0]],
44+
[[2.92, 4.03, 0.0], [0.563706846, 1.033296141, 0.0]],
45+
[[4.228131167, -0.522007766, 0.0], [2.44972507, -1.674799065, 0.0]],
46+
[[4.228131167, -0.522007766, 0.0], [8.663865218, -1.084821998, 0.0]],
47+
[[1.755862468, -1.404991433, 0.0], [2.44972507, -1.674799065, 0.0]],
48+
[[0.563706846, 1.033296141, 0.0], [1.755862468, -1.404991433, 0.0]],
49+
[[-0.139446292, 1.191439787, 0.0], [0.008499564, 1.241560466, 0.0]],
50+
[[0.563706846, 1.033296141, 0.0], [0.008499564, 1.241560466, 0.0]],
51+
]
52+
for act, exp in zip(lines, expected):
53+
sa, ea = act
54+
se, ee = exp
55+
# the line direction sometimes changes ...
56+
assert TOL.is_allclose(sa, se) or TOL.is_allclose(sa, ee)
57+
assert TOL.is_allclose(ea, ee) or TOL.is_allclose(ea, se)

0 commit comments

Comments
 (0)