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 function create_offset_polygons_with_holes_2 #35

Merged
merged 5 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Added recipe hasher.
* Added `scip` to dev install instructions in README.md
* Added `compas_cgal.straight_skeleton_2.create_offset_polygons_with_holes_2`.

### Changed

Expand Down
57 changes: 57 additions & 0 deletions src/compas_cgal/straight_skeleton_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,63 @@ def create_offset_polygons_2(points, offset) -> list[Polygon]:
return [Polygon(points.tolist()) for points in offset_polygons]


def create_offset_polygons_with_holes_2(points, holes, offset) -> list[Tuple[Polygon, list[Polygon]]]:
Copy link
Member

Choose a reason for hiding this comment

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

should have asked this before, but i guess _2 means "2D"? perhaps this should be mentioned in the docstrings...

Copy link
Member Author

Choose a reason for hiding this comment

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

right, i've added it to the docstrings. I've use "_2" as the cgal functions are named like that as well

"""Compute the polygon offset with holes.

Parameters
----------
points : list of point coordinates or :class:`compas.geometry.Polygon`
The points of the polygon.
holes : list of list of point coordinates or list of :class:`compas.geometry.Polygon`
The holes of the polygon.
offset : float
The offset distance. If negative, the offset is outside the polygon, otherwise inside.

Returns
-------

Returns
-------
list of tuple of (:class:`Polygon`, list[:class:`Polygon`])
The polygons with holes.

Raises
------
ValueError
If the normal of the polygon is not [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.

should it be a unit normal, or vertical up?

Copy link
Member Author

Choose a reason for hiding this comment

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

should be vertical up, i've adjusted the docstrings

If the normal of a hole is not [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.

should it be a unit normal, or vertical down?

"""
points = list(points)
normal = normal_polygon(points, True)
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)

H = []
for i, hole in enumerate(holes):
points = list(hole)
Copy link
Member

Choose a reason for hiding this comment

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

why the conversion?

Copy link
Member Author

Choose a reason for hiding this comment

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

right, not needed

normal_hole = normal_polygon(points, True)
if not TOL.is_allclose(normal_hole, [0, 0, -1]):
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)

offset = float(offset)
Copy link
Member

Choose a reason for hiding this comment

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

why the conversion?

Copy link
Member

Choose a reason for hiding this comment

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

in any case, would do this at the beginning of the algorithm. process all input params (if necessary) at the start...

Copy link
Member Author

Choose a reason for hiding this comment

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

you are right, it is not needed

if offset < 0: # outside
offset_polygons = straight_skeleton_2.create_offset_polygons_2_outer_with_holes(V, H, abs(offset))
else: # inside
offset_polygons = straight_skeleton_2.create_offset_polygons_2_inner_with_holes(V, H, offset)

result = []
for points, holes_np in offset_polygons:
polygon = Polygon(points.tolist())
holes = []
for hole in holes_np:
holes.append(Polygon(hole.tolist()))
result.append((polygon, holes))
return result


def create_weighted_offset_polygons_2(points, offset, weights) -> list[Polygon]:
"""Compute the polygon offset with weights.

Expand Down
180 changes: 100 additions & 80 deletions src/straight_skeleton_2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <CGAL/create_offset_polygons_2.h>
#include <CGAL/create_weighted_offset_polygons_from_polygon_with_holes_2.h>
#include <CGAL/create_weighted_straight_skeleton_2.h>
#include <CGAL/create_offset_polygons_from_polygon_with_holes_2.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point;
Expand All @@ -17,6 +18,8 @@ typedef CGAL::Straight_skeleton_2<K>::Halfedge_const_handle Halfedge_const_handl
typedef CGAL::Straight_skeleton_2<K>::Vertex_const_handle Vertex_const_handle;
typedef boost::shared_ptr<Polygon_2> PolygonPtr;
typedef std::vector<PolygonPtr> PolygonPtrVector;
typedef boost::shared_ptr<Polygon_with_holes> PolygonWithHolesPtr;
typedef std::vector<PolygonWithHolesPtr> PolygonWithHolesPtrVector;


std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vector<int>> mesh_data_from_skeleton(boost::shared_ptr<Ss> &iss){
Expand Down Expand Up @@ -62,97 +65,119 @@ std::tuple<compas::RowMatrixXd, std::vector<int>, compas::RowMatrixXi, std::vect
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)));
compas::RowMatrixXd polygon_to_data(Polygon_2 const& poly){
std::size_t n = poly.size();
compas::RowMatrixXd points(n, 3);
int j = 0;
for(typename Polygon_2::Vertex_const_iterator vi = poly.vertices_begin() ; vi != poly.vertices_end() ; ++ vi){
points(j, 0) = (double)(*vi).x();
points(j, 1) = (double)(*vi).y();
points(j, 2) = 0;
j++;
}
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());
return points;
}

return mesh_data_from_skeleton(iss);
};
std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>> polygon_with_holes_to_data(Polygon_with_holes const& polywh){
std::vector<compas::RowMatrixXd> holes;
compas::RowMatrixXd points = polygon_to_data(polywh.outer_boundary());
for(typename Polygon_with_holes::Hole_const_iterator hi = polywh.holes_begin() ; hi != polywh.holes_end() ; ++ hi){
compas::RowMatrixXd hole = polygon_to_data(*hi);
holes.push_back(hole);
}
std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>> result = std::make_tuple(points, holes);
return result;
}

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)
{
Polygon_2 outer;
for (int i = 0; i < V.rows(); i++)
{
outer.push_back(Point(V(i, 0), V(i, 1)));
Polygon_2 data_to_polygon(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)));
}
Polygon_with_holes poly(outer);
return poly;
}

for (auto hit : holes)
{
Polygon_with_holes data_to_polygon_with_holes(Eigen::Ref<const compas::RowMatrixXd> &V, std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes){
Polygon_2 outer = data_to_polygon(V);
Polygon_with_holes poly(outer);
for (auto hit : holes){
compas::RowMatrixXd H = hit;
//Polygon_2 hole = data_to_polygon(*H); // why does this not work?
Polygon_2 hole;
for (int i = 0; i < H.rows(); i++)
{
for (int i = 0; i < H.rows(); i++){
hole.push_back(Point(H(i, 0), H(i, 1)));
}
poly.add_hole(hole);

}
return poly;
}

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 = data_to_polygon(V);
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end());
return mesh_data_from_skeleton(iss);
};

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){
Polygon_with_holes poly = data_to_polygon_with_holes(V, holes);
SsPtr iss = CGAL::create_interior_straight_skeleton_2(poly);
return mesh_data_from_skeleton(iss);
}

std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_inner(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset){
Polygon_2 poly;
for (int i = 0; i < V.rows(); i++){
poly.push_back(Point(V(i, 0), V(i, 1)));
}
Polygon_2 poly = data_to_polygon(V);
PolygonPtrVector offset_polygons = CGAL::create_interior_skeleton_and_offset_polygons_2(offset, poly);

std::vector<compas::RowMatrixXd> result;
for(auto pi = offset_polygons.begin(); pi != offset_polygons.end(); ++pi){
std::size_t n = (*pi)->size();
compas::RowMatrixXd points(n, 3);
int j = 0;
for (auto vi = (*pi)->vertices_begin(); vi != (*pi)->vertices_end(); ++vi){
points(j, 0) = (double)(*vi).x();
points(j, 1) = (double)(*vi).y();
points(j, 2) = 0;
j++;
}
for(typename PolygonPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
compas::RowMatrixXd points = polygon_to_data(**pi);
result.push_back(points);
}
return result;
}

std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_outer(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset){
Polygon_2 poly;
for (int i = 0; i < V.rows(); i++){
poly.push_back(Point(V(i, 0), V(i, 1)));
std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> pmp_create_offset_polygons_2_inner_with_holes(Eigen::Ref<const compas::RowMatrixXd> &V, std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes, double &offset){

Polygon_with_holes poly = data_to_polygon_with_holes(V, holes);
PolygonWithHolesPtrVector offset_polygons = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(offset, poly);

std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> result;
for(typename PolygonWithHolesPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>> polywh_data = polygon_with_holes_to_data(**pi);
result.push_back(polywh_data);
}
return result;
}

std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_outer(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset){
Polygon_2 poly = data_to_polygon(V);
PolygonPtrVector offset_polygons = CGAL::create_exterior_skeleton_and_offset_polygons_2(offset, poly);

std::vector<compas::RowMatrixXd> result;
for(auto pi = offset_polygons.begin(); pi != offset_polygons.end(); ++pi){
std::size_t n = (*pi)->size();
compas::RowMatrixXd points(n, 3);
int j = 0;
for (auto vi = (*pi)->vertices_begin(); vi != (*pi)->vertices_end(); ++vi){
points(j, 0) = (double)(*vi).x();
points(j, 1) = (double)(*vi).y();
points(j, 2) = 0;
j++;
}
for(typename PolygonPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
compas::RowMatrixXd points = polygon_to_data(**pi);
result.push_back(points);
}
return result;
}

std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_inner(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset, Eigen::Ref<const compas::RowMatrixXd> &weights){
Polygon_2 poly;
for (int i = 0; i < V.rows(); i++){
poly.push_back(Point(V(i, 0), V(i, 1)));
std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> pmp_create_offset_polygons_2_outer_with_holes(Eigen::Ref<const compas::RowMatrixXd> &V, std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes, double &offset){
Polygon_with_holes poly = data_to_polygon_with_holes(V, holes);
PolygonWithHolesPtrVector offset_polygons = CGAL::create_exterior_skeleton_and_offset_polygons_with_holes_2(offset, poly);

std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> result;
for(typename PolygonWithHolesPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>> polywh_data = polygon_with_holes_to_data(**pi);
result.push_back(polywh_data);
}
return result;
}

std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_inner(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset, Eigen::Ref<const compas::RowMatrixXd> &weights){
Polygon_2 poly = data_to_polygon(V);
std::vector<double> weights_vec;
for (int i = 0; i < weights.rows(); i++){
weights_vec.push_back(weights(i, 0));
Expand All @@ -161,26 +186,15 @@ std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_inner(Eig
PolygonPtrVector offset_polygons = CGAL::create_offset_polygons_2<Polygon_2>(offset, *iss);

std::vector<compas::RowMatrixXd> result;
for(auto pi = offset_polygons.begin(); pi != offset_polygons.end(); ++pi){
std::size_t n = (*pi)->size();
compas::RowMatrixXd points(n, 3);
int j = 0;
for (auto vi = (*pi)->vertices_begin(); vi != (*pi)->vertices_end(); ++vi){
points(j, 0) = (double)(*vi).x();
points(j, 1) = (double)(*vi).y();
points(j, 2) = 0;
j++;
}
for(typename PolygonPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
compas::RowMatrixXd points = polygon_to_data(**pi);
result.push_back(points);
}
return result;
}

std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_outer(Eigen::Ref<const compas::RowMatrixXd> &V, double &offset, Eigen::Ref<const compas::RowMatrixXd> &weights){
Polygon_2 poly;
for (int i = 0; i < V.rows(); i++){
poly.push_back(Point(V(i, 0), V(i, 1)));
}
Polygon_2 poly = data_to_polygon(V);
std::vector<double> weights_vec;
for (int i = 0; i < weights.rows(); i++){
weights_vec.push_back(weights(i, 0));
Expand All @@ -189,16 +203,8 @@ std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_outer(Eig
PolygonPtrVector offset_polygons = CGAL::create_offset_polygons_2<Polygon_2>(offset, *iss);

std::vector<compas::RowMatrixXd> result;
for(auto pi = offset_polygons.begin(); pi != offset_polygons.end(); ++pi){
std::size_t n = (*pi)->size();
compas::RowMatrixXd points(n, 3);
int j = 0;
for (auto vi = (*pi)->vertices_begin(); vi != (*pi)->vertices_end(); ++vi){
points(j, 0) = (double)(*vi).x();
points(j, 1) = (double)(*vi).y();
points(j, 2) = 0;
j++;
}
for(typename PolygonPtrVector::const_iterator pi = offset_polygons.begin() ; pi != offset_polygons.end() ; ++ pi ){
compas::RowMatrixXd points = polygon_to_data(**pi);
result.push_back(points);
}
return result;
Expand Down Expand Up @@ -229,12 +235,26 @@ void init_straight_skeleton_2(pybind11::module &m)
pybind11::arg("V").noconvert(),
pybind11::arg("offset").noconvert());

submodule.def(
"create_offset_polygons_2_inner_with_holes",
&pmp_create_offset_polygons_2_inner_with_holes,
pybind11::arg("V").noconvert(),
pybind11::arg("holes").noconvert(),
pybind11::arg("offset").noconvert());

submodule.def(
"create_offset_polygons_2_outer",
&pmp_create_offset_polygons_2_outer,
pybind11::arg("V").noconvert(),
pybind11::arg("offset").noconvert());

submodule.def(
"create_offset_polygons_2_outer_with_holes",
&pmp_create_offset_polygons_2_outer_with_holes,
pybind11::arg("V").noconvert(),
pybind11::arg("holes").noconvert(),
pybind11::arg("offset").noconvert());

submodule.def(
"create_weighted_offset_polygons_2_inner",
&pmp_create_weighted_offset_polygons_2_inner,
Expand Down
10 changes: 10 additions & 0 deletions src/straight_skeleton_2.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,20 @@ std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_inner(
Eigen::Ref<const compas::RowMatrixXd> &V,
double &offset);

std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> pmp_create_offset_polygons_2_inner_with_holes(
Eigen::Ref<const compas::RowMatrixXd> &V,
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes,
double &offset);

std::vector<compas::RowMatrixXd> pmp_create_offset_polygons_2_outer(
Eigen::Ref<const compas::RowMatrixXd> &V,
double &offset);

std::vector<std::tuple<compas::RowMatrixXd, std::vector<compas::RowMatrixXd>>> pmp_create_offset_polygons_2_outer_with_holes(
Eigen::Ref<const compas::RowMatrixXd> &V,
std::vector<Eigen::Ref<const compas::RowMatrixXd>> &holes,
double &offset);

std::vector<compas::RowMatrixXd> pmp_create_weighted_offset_polygons_2_inner(
Eigen::Ref<const compas::RowMatrixXd> &V,
double &offset,
Expand Down
Loading
Loading