Skip to content

Commit d5b1bfe

Browse files
committed
Adding straight skeleton with holes
1 parent acd1f64 commit d5b1bfe

9 files changed

+165
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
* Added `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton`.
13+
* Added `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton_with_holes`.
1314

1415
### Changed
1516

71.4 KB
Loading

docs/api/compas_cgal.straight_skeleton_2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ compas_cgal.straight_skeleton_2
99
:nosignatures:
1010

1111
create_interior_straight_skeleton
12+
create_interior_straight_skeleton_with_holes

docs/examples/straight_skeleton_2.rst

+9
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@
99

1010
.. literalinclude:: straight_skeleton_2.py
1111
:language: python
12+
13+
14+
.. figure:: /_images/cgal_straight_skeleton_2_holes.png
15+
:figclass: figure
16+
:class: figure-img img-fluid
17+
18+
19+
.. literalinclude:: straight_skeleton_2_holes.py
20+
:language: python
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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_with_holes
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+
20+
holes = [
21+
[(0.42, 0.88, 0.0), (1.1, -1.0, 0.0), (-1.97, -0.93, 0.0), (-1.25, 1.82, 0.0)],
22+
[(4.25, -0.64, 0.0), (2.9, -3.03, 0.0), (2.12, -2.16, 0.0), (2.89, -0.36, 0.0)],
23+
[(10.6, 0.29, 0.0), (9.48, -1.54, 0.0), (5.48, -1.26, 0.0), (5.98, -0.04, 0.0)],
24+
]
25+
26+
27+
polygon = Polygon(points)
28+
holes = [Polygon(hole) for hole in holes]
29+
lines = create_interior_straight_skeleton_with_holes(polygon, holes)
30+
graph = Graph.from_lines(lines)
31+
32+
# ==============================================================================
33+
# Viz
34+
# ==============================================================================
35+
36+
viewer = Viewer(width=1600, height=900)
37+
viewer.renderer_config.show_grid = False
38+
viewer.scene.add(graph, edgecolor=(1.0, 0.0, 0.0))
39+
viewer.scene.add(polygon)
40+
for hole in holes:
41+
viewer.scene.add(hole)
42+
viewer.show()

src/compas_cgal/straight_skeleton_2.py

+36
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,39 @@ def create_interior_straight_skeleton(points) -> PolylinesNumpy:
3030
raise ValueError("Please pass a polygon with a normal vector of [0, 0, 1].")
3131
V = np.asarray(points, dtype=np.float64)
3232
return straight_skeleton_2.create_interior_straight_skeleton(V)
33+
34+
35+
def create_interior_straight_skeleton_with_holes(points, holes) -> PolylinesNumpy:
36+
"""Compute the skeleton of a polygon with holes.
37+
38+
Parameters
39+
----------
40+
points : list of point coordinates or :class:`compas.geometry.Polygon`
41+
The points of the polygon.
42+
holes : list of list of point coordinates or list of :class:`compas.geometry.Polygon`
43+
The holes of the polygon.
44+
45+
Returns
46+
-------
47+
:attr:`compas_cgal.types.PolylinesNumpy`
48+
The skeleton of the polygon.
49+
50+
Raises
51+
------
52+
ValueError
53+
If the normal of the polygon is not [0, 0, 1].
54+
If the normal of a hole is not [0, 0, -1].
55+
"""
56+
points = list(points)
57+
if not TOL.is_allclose(normal_polygon(points, True), [0, 0, 1]):
58+
raise ValueError("Please pass a polygon with a normal vector of [0, 0, 1].")
59+
H = []
60+
for hole in holes:
61+
points = list(hole)
62+
if not TOL.is_allclose(normal_polygon(points, True), [0, 0, -1]):
63+
raise ValueError("Please pass a hole with a normal vector of [0, 0, -1].")
64+
hole = np.asarray(points, dtype=np.float64)
65+
H.append(hole)
66+
67+
V = np.asarray(points, dtype=np.float64)
68+
return straight_skeleton_2.create_interior_straight_skeleton_with_holes(V, H)

src/straight_skeleton_2.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
#include "straight_skeleton_2.h"
33
#include <CGAL/Polygon_2.h>
44
#include <CGAL/create_straight_skeleton_2.h>
5+
#include <CGAL/create_straight_skeleton_from_polygon_with_holes_2.h>
56

67
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
78
typedef K::Point_2 Point;
89
typedef CGAL::Polygon_2<K> Polygon_2;
10+
typedef CGAL::Polygon_with_holes_2<K> Polygon_with_holes;
911
typedef CGAL::Straight_skeleton_2<K> Ss;
1012
typedef boost::shared_ptr<Ss> SsPtr;
1113
typedef CGAL::Straight_skeleton_2<K>::Halfedge_const_handle Halfedge_const_handle;
@@ -39,6 +41,50 @@ compas::Edges pmp_create_interior_straight_skeleton(
3941
return edgelist;
4042
};
4143

44+
compas::Edges pmp_create_interior_straight_skeleton_with_holes(
45+
Eigen::Ref<const compas::RowMatrixXd> &V,
46+
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes)
47+
{
48+
Polygon_2 outer;
49+
for (int i = 0; i < V.rows(); i++)
50+
{
51+
outer.push_back(Point(V(i, 0), V(i, 1)));
52+
}
53+
Polygon_with_holes poly(outer);
54+
55+
56+
for (auto hit : holes)
57+
{
58+
compas::RowMatrixXd H = hit;
59+
Polygon_2 hole;
60+
for (int i = 0; i < H.rows(); i++)
61+
{
62+
hole.push_back(Point(H(i, 0), H(i, 1)));
63+
}
64+
poly.add_hole(hole);
65+
66+
}
67+
68+
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly);
69+
compas::Edges edgelist;
70+
for(auto hit = iss->halfedges_begin(); hit != iss->halfedges_end(); ++hit){
71+
const Halfedge_const_handle h = hit;
72+
if(!h->is_bisector()){
73+
continue;
74+
}
75+
const Vertex_const_handle& v1 = h->vertex();
76+
const Vertex_const_handle& v2 = h->opposite()->vertex();
77+
if(&*v1 < &*v2){
78+
std::vector<double> s_vec = {v1->point().x(), v1->point().y(), 0};
79+
std::vector<double> t_vec = {v2->point().x(), v2->point().y(), 0};
80+
compas::Edge edge = std::make_tuple(s_vec, t_vec);
81+
edgelist.push_back(edge);
82+
}
83+
84+
}
85+
return edgelist;
86+
87+
}
4288

4389
// ===========================================================================
4490
// PyBind11
@@ -52,4 +98,10 @@ void init_straight_skeleton_2(pybind11::module &m)
5298
"create_interior_straight_skeleton",
5399
&pmp_create_interior_straight_skeleton,
54100
pybind11::arg("V").noconvert());
101+
102+
submodule.def(
103+
"create_interior_straight_skeleton_with_holes",
104+
&pmp_create_interior_straight_skeleton_with_holes,
105+
pybind11::arg("V").noconvert(),
106+
pybind11::arg("holes").noconvert());
55107
};

src/straight_skeleton_2.h

+5
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
compas::Edges pmp_create_interior_straight_skeleton(
88
Eigen::Ref<const compas::RowMatrixXd> &V);
99

10+
11+
compas::Edges pmp_create_interior_straight_skeleton_with_holes(
12+
Eigen::Ref<const compas::RowMatrixXd> &V,
13+
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes);
14+
1015
#endif /* COMPAS_STRAIGHT_SKELETON_2_H */

tests/test_straight_skeleton_2.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
21
from compas.tolerance import TOL
32

3+
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
4+
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton_with_holes
5+
46

57
def test_straight_polygon():
68
points = [
@@ -17,6 +19,22 @@ def test_straight_polygon():
1719
assert len(lines) == 8
1820

1921

22+
def test_straight_skeleton_with_holes():
23+
points = [
24+
(-1, -1, 0),
25+
(0, -12, 0),
26+
(1, -1, 0),
27+
(12, 0, 0),
28+
(1, 1, 0),
29+
(0, 12, 0),
30+
(-1, 1, 0),
31+
(-12, 0, 0),
32+
]
33+
hole = [(-1, 0, 0), (0, 1, 0), (1, 0, 0), (0, -1, 0)]
34+
lines = create_interior_straight_skeleton_with_holes(points, [hole])
35+
assert len(lines) == 20
36+
37+
2038
def test_straight_polygon_2_compare():
2139
points = [
2240
(-1.91, 3.59, 0.0),

0 commit comments

Comments
 (0)