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

Changing straight skeleton return data into Graph #33

Merged
merged 11 commits into from
Sep 18, 2024
Merged
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## Unreleased

### Added

### Changed

* Changed the return values of `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton` and `compas_cgal.straight_skeleton_2.create_interior_straight_skeleton_with_holes`.
* Changed the return values of `compas_cgal.create_interior_straight_skeleton`.

### Removed


## [0.7.0] 2024-05-14

### Added
19 changes: 12 additions & 7 deletions docs/examples/straight_skeleton_2.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from compas.datastructures import Graph
from compas.geometry import Polygon
from compas_cgal.straight_skeleton_2 import create_interior_straight_skeleton
from compas_viewer import Viewer

@@ -15,15 +13,22 @@
(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)


graph = create_interior_straight_skeleton(points)

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

viewer = Viewer(width=1600, height=900)
viewer.scene.add(graph, edgecolor=(1.0, 0.0, 0.0))
viewer.scene.add(polygon)
for edge in graph.edges():
line = graph.edge_line(edge)
if graph.edge_attribute(edge, "inner_bisector"):
print(edge, "inner_bisector")
viewer.add(line, linecolor=(1.0, 0.0, 0.0), linewidth=2)
elif graph.edge_attribute(edge, "bisector"):
viewer.add(line, linecolor=(0.0, 0.0, 1.0))
else:
viewer.add(line, linecolor=(0.0, 0.0, 0.0))
viewer.show()
18 changes: 10 additions & 8 deletions docs/examples/straight_skeleton_2_holes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from compas.datastructures import Graph
from compas.geometry import Polygon
from compas_viewer import Viewer

@@ -26,17 +25,20 @@

polygon = Polygon(points)
holes = [Polygon(hole) for hole in holes]
lines = create_interior_straight_skeleton_with_holes(polygon, holes)
graph = Graph.from_lines(lines)
graph = create_interior_straight_skeleton_with_holes(polygon, holes)

# ==============================================================================
# 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)
for hole in holes:
viewer.scene.add(hole)

for edge in graph.edges():
line = graph.edge_line(edge)
if graph.edge_attribute(edge, "inner_bisector"):
viewer.add(line, linecolor=(1.0, 0.0, 0.0), linewidth=2)
elif graph.edge_attribute(edge, "bisector"):
viewer.add(line, linecolor=(0.0, 0.0, 1.0))
else:
viewer.add(line, linecolor=(0.0, 0.0, 0.0))
viewer.show()
64 changes: 57 additions & 7 deletions src/compas_cgal/straight_skeleton_2.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,66 @@
from typing import Tuple
from typing import Union

import numpy as np
from compas.datastructures import Graph
from compas.geometry import Polygon
from compas.geometry import normal_polygon
from compas.tolerance import TOL

from compas_cgal._cgal import straight_skeleton_2

from .types import PolylinesNumpy
from .types import IntNx1
from .types import IntNx2
from .types import VerticesNumpy


def graph_from_skeleton_data(points: VerticesNumpy, indices: IntNx1, edges: IntNx2, edge_types: IntNx1) -> Graph:
"""Create a graph from the skeleton data.

Parameters
----------
points : :class:`numpy.ndarray`
The vertices of the skeleton, each vertex defined by 3 spatial coordinates.
indices : :class:`numpy.ndarray`
The vertex indices of the skeleton, corresponding to the points.
edges : :class:`numpy.ndarray`
The edges of the skeleton, each edge defined by 2 vertex indices.
edge_types : :class:`numpy.ndarray`
The type per edge, `0` for inner bisector, `1` for bisector, and `2` for boundary.

def create_interior_straight_skeleton(points) -> PolylinesNumpy:
Returns
-------
:class:`compas.datastructures.Graph`
The skeleton as a graph.
"""
graph = Graph()
for pt, i in zip(points, indices):
graph.add_node(key=i, x=pt[0], y=pt[1], z=pt[2])

for edge, etype in zip(edges, edge_types):
edge = graph.add_edge(*edge)
if etype == 0:
graph.edge_attribute(edge, "inner_bisector", True)
elif etype == 1:
graph.edge_attribute(edge, "bisector", True)
else:
graph.edge_attribute(edge, "boundary", True)
return graph


def create_interior_straight_skeleton(points, as_graph=True) -> Union[Graph, Tuple[VerticesNumpy, IntNx1, IntNx2, IntNx1]]:
"""Compute the skeleton of a polygon.

Parameters
----------
points : list of point coordinates or :class:`compas.geometry.Polygon`
The points of the polygon.
as_graph : bool, optional
Whether the skeleton should be returned as a graph, defaults to `True`.

Returns
-------
:attr:`compas_cgal.types.PolylinesNumpy`
:attr:`compas.datastructures.Graph` or tuple of (vertices, indices, edges, edge_types)
The skeleton of the polygon.

Raises
@@ -31,10 +73,13 @@ def create_interior_straight_skeleton(points) -> PolylinesNumpy:
if not TOL.is_allclose(normal, [0, 0, 1]):
raise ValueError("The normal of the polygon should be [0, 0, 1]. The normal of the provided polygon is {}".format(normal))
V = np.asarray(points, dtype=np.float64)
return straight_skeleton_2.create_interior_straight_skeleton(V)
points, indices, edges, edge_types = straight_skeleton_2.create_interior_straight_skeleton(V)
if as_graph:
return graph_from_skeleton_data(points, indices, edges, edge_types)
return points, indices, edges, edge_types


def create_interior_straight_skeleton_with_holes(points, holes) -> PolylinesNumpy:
def create_interior_straight_skeleton_with_holes(points, holes, as_graph=True) -> Union[Graph, Tuple[VerticesNumpy, IntNx1, IntNx2, IntNx1]]:
"""Compute the skeleton of a polygon with holes.

Parameters
@@ -43,10 +88,12 @@ def create_interior_straight_skeleton_with_holes(points, holes) -> PolylinesNump
The points of the polygon.
holes : list of list of point coordinates or list of :class:`compas.geometry.Polygon`
The holes of the polygon.
as_graph : bool, optional
Whether the skeleton should be returned as a graph, defaults to `True`.

Returns
-------
:attr:`compas_cgal.types.PolylinesNumpy`
:attr:`compas.datastructures.Graph` or tuple of (vertices, indices, edges, edge_types)
The skeleton of the polygon.

Raises
@@ -69,7 +116,10 @@ def create_interior_straight_skeleton_with_holes(points, holes) -> PolylinesNump
raise ValueError("The normal of the hole should be [0, 0, -1]. The normal of the provided {}-th hole is {}".format(i, normal_hole))
hole = np.asarray(points, dtype=np.float64)
H.append(hole)
return straight_skeleton_2.create_interior_straight_skeleton_with_holes(V, H)
points, indices, edges, edge_types = straight_skeleton_2.create_interior_straight_skeleton_with_holes(V, H)
if as_graph:
return graph_from_skeleton_data(points, indices, edges, edge_types)
return points, indices, edges, edge_types


def create_offset_polygons_2(points, offset) -> list[Polygon]:
2 changes: 2 additions & 0 deletions src/compas_cgal/types.py
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@

FloatNx3 = Annotated[NDArray[float64], Literal["N", 3]]
IntNx3 = Annotated[NDArray[int64], Literal["N", 3]]
IntNx2 = Annotated[NDArray[int64], Literal["N", 2]]
IntNx1 = Annotated[NDArray[int64], Literal["N", 1]]

VerticesNumpy = FloatNx3
"""An array of vertices, with each vertex defined by 3 spatial coordinates."""
85 changes: 48 additions & 37 deletions src/straight_skeleton_2.cpp
Original file line number Diff line number Diff line change
@@ -18,35 +18,64 @@ typedef CGAL::Straight_skeleton_2<K>::Vertex_const_handle Vertex_const_handle;
typedef boost::shared_ptr<Polygon_2> PolygonPtr;
typedef std::vector<PolygonPtr> PolygonPtrVector;

compas::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)));

std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> mesh_data_from_skeleton(boost::shared_ptr<Ss> &iss){
std::size_t v = iss->size_of_vertices();
std::size_t e = iss->size_of_halfedges() / 2; // halfedges are stored twice

compas::RowMatrixXd Mv(v, 3);
std::vector<int> Mvi; // to save the vertex ids
compas::RowMatrixXi Me(e, 2);
std::vector<int> Mei; // to save the edge type: 0: inner bisector, 1: bisector, 2: boundary

std::size_t i = 0;
for(auto hit = iss->vertices_begin(); hit != iss->vertices_end(); ++hit){
const Vertex_const_handle vh = hit;
Mv(i, 0) = (double)vh->point().x();
Mv(i, 1) = (double)vh->point().y();
Mv(i, 2) = 0;
Mvi.push_back((int)vh->id());
i++;
}
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());
compas::Edges edgelist;
i = 0;
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};
compas::Edge edge = std::make_tuple(s_vec, t_vec);
edgelist.push_back(edge);
Me(i, 0) = (int)v1->id();
Me(i, 1) = (int)v2->id();

if(h->is_inner_bisector()){
Mei.push_back(0);
}
else if(h->is_bisector()){
Mei.push_back(1);
}else{
Mei.push_back(2);
}
i++;
}
}
std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> result = std::make_tuple(Mv, Mvi, Me, Mei);
return result;
}

std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> 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)));
}
return edgelist;
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());

return mesh_data_from_skeleton(iss);
};

compas::Edges pmp_create_interior_straight_skeleton_with_holes(
std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> pmp_create_interior_straight_skeleton_with_holes(
Eigen::Ref<const compas::RowMatrixXd> &V,
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes)
{
@@ -57,7 +86,6 @@ compas::Edges pmp_create_interior_straight_skeleton_with_holes(
}
Polygon_with_holes poly(outer);


for (auto hit : holes)
{
compas::RowMatrixXd H = hit;
@@ -71,24 +99,7 @@ compas::Edges pmp_create_interior_straight_skeleton_with_holes(
}

SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly);
compas::Edges edgelist;
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};
compas::Edge edge = std::make_tuple(s_vec, t_vec);
edgelist.push_back(edge);
}

}
return edgelist;

return mesh_data_from_skeleton(iss);
}

std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_inner(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset){
4 changes: 2 additions & 2 deletions src/straight_skeleton_2.h
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@
#include <compas.h>


compas::Edges pmp_create_interior_straight_skeleton(
std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> pmp_create_interior_straight_skeleton(
Eigen::Ref<const compas::RowMatrixXd> &V);


compas::Edges pmp_create_interior_straight_skeleton_with_holes(
std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> pmp_create_interior_straight_skeleton_with_holes(
Eigen::Ref<const compas::RowMatrixXd> &V,
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes);

Loading