diff --git a/CHANGELOG.md b/CHANGELOG.md index 62ed11ae..399a6465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,10 +18,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Tests from 2D straight skeleton module. + ### Changed +* Added polygon with holes offset test. + ### Removed +* Profiling dependency and decorators from examples. + ## [0.7.7] 2025-03-20 diff --git a/docs/examples/example_booleans.py b/docs/examples/example_booleans.py index 2b17f08d..c73c89d6 100644 --- a/docs/examples/example_booleans.py +++ b/docs/examples/example_booleans.py @@ -4,7 +4,6 @@ from compas.geometry import Sphere from compas.geometry import Translation from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.booleans import boolean_difference_mesh_mesh from compas_cgal.booleans import boolean_intersection_mesh_mesh @@ -59,7 +58,6 @@ def split(): return mesh.exploded() -@profile def main(): """Compute the boolean difference, intersection, union, and split of two triangle meshes.""" difference = boolean_difference() diff --git a/docs/examples/example_intersections.py b/docs/examples/example_intersections.py index 3bcbbbe1..3b0dbe14 100644 --- a/docs/examples/example_intersections.py +++ b/docs/examples/example_intersections.py @@ -1,17 +1,13 @@ -import profile - from compas.datastructures import Mesh from compas.geometry import Box from compas.geometry import Point from compas.geometry import Polyline from compas.geometry import Sphere from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.intersections import intersection_mesh_mesh -@profile def main(): # ============================================================================== # Make a box and a sphere diff --git a/docs/examples/example_measure.py b/docs/examples/example_measure.py index f991bbbb..3e6f22b8 100644 --- a/docs/examples/example_measure.py +++ b/docs/examples/example_measure.py @@ -1,13 +1,11 @@ from compas.datastructures import Mesh from compas.geometry import Box -from line_profiler import profile from compas_cgal.measure import mesh_area from compas_cgal.measure import mesh_centroid from compas_cgal.measure import mesh_volume -@profile def main(mesh): """Mesh measurement methods.""" area = mesh_area(mesh) diff --git a/docs/examples/example_meshing.py b/docs/examples/example_meshing.py index b69f5761..a870efc4 100644 --- a/docs/examples/example_meshing.py +++ b/docs/examples/example_meshing.py @@ -9,12 +9,10 @@ from compas.geometry import scale_vector from compas.geometry import transform_points_numpy from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.meshing import mesh_remesh -@profile def main(): """Remesh a bunny mesh that is loaded from .ply file.""" @@ -30,7 +28,7 @@ def main(): S = Scale.from_factors([100, 100, 100]) R = Rotation.from_axis_and_angle(Vector(1, 0, 0), math.radians(90)) - bunny.transform(R * S * T) + # bunny.transform(R * S * T) V0, F0 = bunny.to_vertices_and_faces() V1 = transform_points_numpy(V0, R * S * T) diff --git a/docs/examples/example_reconstruction_pointset_normal_estimation.py b/docs/examples/example_reconstruction_pointset_normal_estimation.py index fb8f2b7f..f9fe6c56 100644 --- a/docs/examples/example_reconstruction_pointset_normal_estimation.py +++ b/docs/examples/example_reconstruction_pointset_normal_estimation.py @@ -5,13 +5,11 @@ from compas_viewer import Viewer from compas_viewer.config import Config from compas_viewer.scene import Collection -from line_profiler import profile from compas_cgal.reconstruction import pointset_normal_estimation from compas_cgal.reconstruction import pointset_reduction -@profile def reconstruction_pointset_normal_estimation(): # ============================================================================== # Input geometry diff --git a/docs/examples/example_reconstruction_pointset_outlier_removal.py b/docs/examples/example_reconstruction_pointset_outlier_removal.py index e5ca5889..26ce344b 100644 --- a/docs/examples/example_reconstruction_pointset_outlier_removal.py +++ b/docs/examples/example_reconstruction_pointset_outlier_removal.py @@ -3,12 +3,10 @@ from compas.geometry import Pointcloud from compas_viewer import Viewer from compas_viewer.config import Config -from line_profiler import profile from compas_cgal.reconstruction import pointset_outlier_removal -@profile def reconstruction_pointset_outlier_removal(): """Remove outliers from a point set.""" diff --git a/docs/examples/example_reconstruction_pointset_reduction.py b/docs/examples/example_reconstruction_pointset_reduction.py index 8a5160cd..ab831a1a 100644 --- a/docs/examples/example_reconstruction_pointset_reduction.py +++ b/docs/examples/example_reconstruction_pointset_reduction.py @@ -5,12 +5,10 @@ from compas.geometry import transform_points_numpy from compas_viewer import Viewer from compas_viewer.config import Config -from line_profiler import profile from compas_cgal.reconstruction import pointset_reduction -@profile def reconstruction_pointset_reduction(): """Reduce the number of points in a point set.""" diff --git a/docs/examples/example_reconstruction_pointset_smoothing.py b/docs/examples/example_reconstruction_pointset_smoothing.py index c2ecf752..540fe34c 100644 --- a/docs/examples/example_reconstruction_pointset_smoothing.py +++ b/docs/examples/example_reconstruction_pointset_smoothing.py @@ -1,16 +1,12 @@ from pathlib import Path from compas.geometry import Pointcloud -from compas.geometry import Translation -from compas.geometry import transform_points_numpy from compas_viewer import Viewer from compas_viewer.config import Config -from line_profiler import profile from compas_cgal.reconstruction import pointset_smoothing -@profile def reconstruction_pointset_smoothing(): """Smooth a point set.""" diff --git a/docs/examples/example_reconstruction_poisson_surface_reconstruction.py b/docs/examples/example_reconstruction_poisson_surface_reconstruction.py index b3f1a13e..02f3d2ea 100644 --- a/docs/examples/example_reconstruction_poisson_surface_reconstruction.py +++ b/docs/examples/example_reconstruction_poisson_surface_reconstruction.py @@ -1,17 +1,12 @@ -import math from pathlib import Path from compas.datastructures import Mesh from compas.geometry import Pointcloud -from compas.geometry import Rotation -from compas.geometry import Scale from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.reconstruction import poisson_surface_reconstruction -@profile def reconstruction_poisson_surface_reconstruction(): FILE = Path(__file__).parent.parent.parent / "data" / "oni.xyz" diff --git a/docs/examples/example_skeletonization.py b/docs/examples/example_skeletonization.py index 03d417ab..5d2d69bf 100644 --- a/docs/examples/example_skeletonization.py +++ b/docs/examples/example_skeletonization.py @@ -7,12 +7,10 @@ from compas.geometry import Scale from compas.geometry import Translation from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.skeletonization import mesh_skeleton -@profile def main(): """Skeletonize a mesh.""" diff --git a/docs/examples/example_slicer.py b/docs/examples/example_slicer.py index 65be5169..94c51a8a 100644 --- a/docs/examples/example_slicer.py +++ b/docs/examples/example_slicer.py @@ -1,4 +1,3 @@ -import math from pathlib import Path import numpy as np @@ -9,12 +8,10 @@ from compas.geometry import Vector from compas_viewer import Viewer from compas_viewer.config import Config -from line_profiler import profile from compas_cgal.slicer import slice_mesh -@profile def main(): # Get Mesh from STL FILE = Path(__file__).parent.parent.parent / "data" / "3DBenchy.stl" diff --git a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton.py b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton.py index 387b0290..75807367 100644 --- a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton.py +++ b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton.py @@ -1,12 +1,10 @@ from compas.geometry import Point from compas.geometry import Polygon from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.straight_skeleton_2 import interior_straight_skeleton -@profile def main(): """Compute the interior straight skeleton of a polygon.""" diff --git a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py index 61b6e030..cb67afc1 100644 --- a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py +++ b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py @@ -1,11 +1,10 @@ from compas.geometry import Polygon from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.straight_skeleton_2 import offset_polygon +from compas_cgal.straight_skeleton_2 import offset_polygon_with_holes -@profile def main(): """Create offset polygons.""" @@ -27,23 +26,32 @@ def main(): offset_polygon_inner = offset_polygon(points, offset) offset_polygon_outer = offset_polygon(points, -offset) - return offset_polygon_inner, offset_polygon_outer, polygon + result = offset_polygon_with_holes(offset_polygon_outer[0], offset_polygon_inner, -0.1) -offset_polygon_inner, offset_polygon_outer, polygon = main() + + return offset_polygon_inner, offset_polygon_outer, polygon, result + + +offset_polygon_inner, offset_polygon_outer, polygon, result = main() # ============================================================================== # Visualize # ============================================================================== viewer = Viewer() -viewer.scene.add(polygon) +viewer.scene.add(polygon, show_faces=False) viewer.config.renderer.show_grid = False for opolygon in offset_polygon_inner: - viewer.scene.add(opolygon, linecolor=(1.0, 0.0, 0.0), facecolor=(1.0, 1.0, 1.0, 0.0)) + viewer.scene.add(opolygon, linecolor=(1.0, 0.0, 0.0), facecolor=(1.0, 0.0, 0.0, 0.0), show_faces=False) for opolygon in offset_polygon_outer: - viewer.scene.add(opolygon, linecolor=(0.0, 0.0, 1.0), facecolor=(1.0, 1.0, 1.0, 0.0)) + viewer.scene.add(opolygon, linecolor=(0.0, 0.0, 1.0), facecolor=(0.0, 0.0, 1.0, 0.0), show_faces=False) + +for opolygon, holes in result: + viewer.scene.add(opolygon, linecolor=(0.0, 0.0, 1.0), facecolor=(0.0, 0.0, 1.0, 0.0), show_faces=False) + for hole in holes: + viewer.scene.add(hole, linecolor=(0.0, 0.0, 1.0), facecolor=(0.0, 0.0, 1.0, 0.0), show_faces=False) -viewer.show() +viewer.show() \ No newline at end of file diff --git a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py index 320ed764..64d3a899 100644 --- a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py +++ b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py @@ -1,11 +1,9 @@ from compas.geometry import Polygon from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.straight_skeleton_2 import weighted_offset_polygon -@profile def main(): """Create weighted offset polygons.""" diff --git a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_with_holes.py b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_with_holes.py index 2ec4b88c..4a4085eb 100644 --- a/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_with_holes.py +++ b/docs/examples/example_straight_skeleton_2_interior_straight_skeleton_with_holes.py @@ -1,11 +1,9 @@ from compas.geometry import Polygon from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.straight_skeleton_2 import interior_straight_skeleton_with_holes -@profile def main(): """Compute the interior straight skeleton of a polygon with holes.""" diff --git a/docs/examples/example_subdivision.py b/docs/examples/example_subdivision.py index 423add23..95803753 100644 --- a/docs/examples/example_subdivision.py +++ b/docs/examples/example_subdivision.py @@ -2,14 +2,12 @@ from compas.geometry import Polyhedron from compas.geometry import Translation from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.subdivision import mesh_subdivide_catmull_clark from compas_cgal.subdivision import mesh_subdivide_loop from compas_cgal.subdivision import mesh_subdivide_sqrt3 -@profile def main(): """Subdivide a mesh using different subdivision algorithms.""" diff --git a/docs/examples/example_triangulation.py b/docs/examples/example_triangulation.py index 522a4642..87cfc95a 100644 --- a/docs/examples/example_triangulation.py +++ b/docs/examples/example_triangulation.py @@ -2,13 +2,11 @@ from compas.geometry import Polygon from compas.geometry import Translation from compas_viewer import Viewer -from line_profiler import profile from compas_cgal.triangulation import conforming_delaunay_triangulation from compas_cgal.triangulation import refined_delaunay_mesh -@profile def main(): """Triangulate a mesh with holes.""" diff --git a/requirements-dev.txt b/requirements-dev.txt index 8d4c2d08..6aea5c73 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,6 @@ bump-my-version compas_invocations2 compas_viewer invoke >=0.14 -line_profiler >=4.0.0 pytest >=7.0.0 pytest-cov >=4.0.0 nanobind >=1.3.2 diff --git a/src/compas_cgal/straight_skeleton_2.py b/src/compas_cgal/straight_skeleton_2.py index 9e9c37c2..d328a0ab 100644 --- a/src/compas_cgal/straight_skeleton_2.py +++ b/src/compas_cgal/straight_skeleton_2.py @@ -181,6 +181,11 @@ def offset_polygon_with_holes(points, holes, offset) -> list[Tuple[Polygon, list """ points = list(points) normal = normal_polygon(points, True) + + if TOL.is_allclose(normal, [0, 0, -1]): + points.reverse() + normal *= -1 + 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, order="C") @@ -189,8 +194,14 @@ def offset_polygon_with_holes(points, holes, offset) -> list[Tuple[Polygon, list for i, hole in enumerate(holes): points = hole normal_hole = normal_polygon(points, True) + + if TOL.is_allclose(normal_hole, [0, 0, 1]): + points.points.reverse() + normal_hole *= -1 + 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, order="C") H.append(hole) @@ -200,10 +211,10 @@ def offset_polygon_with_holes(points, holes, offset) -> list[Tuple[Polygon, list 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()) + for points_list in offset_polygons: + polygon = Polygon(points_list[0].tolist()) holes = [] - for hole in holes_np: + for hole in points_list[1:]: holes.append(Polygon(hole.tolist())) result.append((polygon, holes)) return result diff --git a/src/straight_skeleton_2.cpp b/src/straight_skeleton_2.cpp index b8f8a799..0819cc70 100644 --- a/src/straight_skeleton_2.cpp +++ b/src/straight_skeleton_2.cpp @@ -159,7 +159,7 @@ pmp_create_offset_polygons_2_inner( return result; } -std::vector>> +std::vector> pmp_create_offset_polygons_2_inner_with_holes( const compas::RowMatrixXd& boundary_vertices, const std::vector& hole_vertices, @@ -169,9 +169,19 @@ pmp_create_offset_polygons_2_inner_with_holes( PolygonWithHolesPtrVector offset_polygons = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2( offset_distance, polygon); - std::vector>> result; - for(const auto& offset_polygon : offset_polygons) { - result.push_back(polygon_with_holes_to_data(*offset_polygon)); + std::vector> result; + for(const PolygonWithHolesPtr& offset_polygon : offset_polygons) { + + + std::vector polygon_with_holes; + + polygon_with_holes.push_back(polygon_to_data(offset_polygon->outer_boundary())); + + for(const auto& hole : offset_polygon->holes()) { + polygon_with_holes.push_back(polygon_to_data(hole)); + } + + result.push_back(polygon_with_holes); } return result; } @@ -192,7 +202,7 @@ pmp_create_offset_polygons_2_outer( return result; } -std::vector>> +std::vector> pmp_create_offset_polygons_2_outer_with_holes( const compas::RowMatrixXd& boundary_vertices, const std::vector& hole_vertices, @@ -202,9 +212,14 @@ pmp_create_offset_polygons_2_outer_with_holes( PolygonWithHolesPtrVector offset_polygons = CGAL::create_exterior_skeleton_and_offset_polygons_with_holes_2( offset_distance, polygon); - std::vector>> result; + std::vector> result; for(const auto& offset_polygon : offset_polygons) { - result.push_back(polygon_with_holes_to_data(*offset_polygon)); + std::vector polygon_with_holes; + polygon_with_holes.push_back(polygon_to_data(offset_polygon->outer_boundary())); + for(const auto& hole : offset_polygon->holes()) { + polygon_with_holes.push_back(polygon_to_data(hole)); + } + result.push_back(polygon_with_holes); } return result; } diff --git a/src/straight_skeleton_2.h b/src/straight_skeleton_2.h index 126c0d7f..748093db 100644 --- a/src/straight_skeleton_2.h +++ b/src/straight_skeleton_2.h @@ -66,12 +66,18 @@ pmp_create_offset_polygons_2_inner( * @return std::vector>> Vector containing: * - For each offset: tuple of outer polygon (Px2, float64) and vector of inner polygons (each Qx2, float64) */ -std::vector>> +std::vector> pmp_create_offset_polygons_2_inner_with_holes( const compas::RowMatrixXd& boundary_vertices, const std::vector& hole_vertices, double& offset_distance ); +// std::vector>> +// pmp_create_offset_polygons_2_inner_with_holes( +// const compas::RowMatrixXd& boundary_vertices, +// const std::vector& hole_vertices, +// double& offset_distance +// ); /** * @brief Creates outward offset polygons from a simple polygon. @@ -92,10 +98,10 @@ pmp_create_offset_polygons_2_outer( * @param boundary_vertices Matrix of boundary polygon vertices as Nx2 matrix in row-major order (float64) * @param hole_vertices Vector of hole polygons, each as Mx2 matrix in row-major order (float64) * @param offset_distance Offset distance (positive for inward, negative for outward) - * @return std::vector>> Vector containing: + * @return std::vector> Vector containing: * - For each offset: tuple of outer polygon (Px2, float64) and vector of inner polygons (each Qx2, float64) */ -std::vector>> +std::vector> pmp_create_offset_polygons_2_outer_with_holes( const compas::RowMatrixXd& boundary_vertices, const std::vector& hole_vertices, diff --git a/src/types_std.cpp b/src/types_std.cpp index 739582f4..f4c786bd 100644 --- a/src/types_std.cpp +++ b/src/types_std.cpp @@ -6,5 +6,6 @@ NB_MODULE(_types_std, m) { nb::bind_vector>(m, "VectorInt"); nb::bind_vector>>(m, "VectorVectorInt"); nb::bind_vector>(m, "VectorRowMatrixXd"); + nb::bind_vector>>(m, "VectorVectorRowMatrixXd"); } \ No newline at end of file diff --git a/tests/test_straight_skeleton_2_interior_straight_skeleton.py b/tests/test_straight_skeleton_2_interior_straight_skeleton.py new file mode 100644 index 00000000..3cfa915b --- /dev/null +++ b/tests/test_straight_skeleton_2_interior_straight_skeleton.py @@ -0,0 +1,34 @@ +from compas.geometry import Point, Polygon +from compas_cgal.straight_skeleton_2 import interior_straight_skeleton + + +def test_interior_straight_skeleton(): + # Define polygon points + points = [ + Point(-1.91, 3.59, 0.0), + Point(-5.53, -5.22, 0.0), + Point(-0.39, -1.98, 0.0), + Point(2.98, -5.51, 0.0), + Point(4.83, -2.02, 0.0), + Point(9.70, -3.63, 0.0), + Point(12.23, 1.25, 0.0), + Point(3.42, 0.66, 0.0), + Point(2.92, 4.03, 0.0), + Point(-1.91, 3.59, 0.0), + ] + polygon = Polygon(points) + + # Compute skeleton + graph = interior_straight_skeleton(polygon) + + # Assertions + assert graph is not None, "Expected a graph object" + assert graph.number_of_nodes() > 0, "Graph should have nodes" + assert graph.number_of_edges() > 0, "Graph should have edges" + + for edge in graph.edges(): + line = graph.edge_line(edge) + assert line, f"Edge {edge} should return a valid line" + assert hasattr(line, "length"), "Line should have a length attribute" + length = line.length + assert length > 0, "Skeleton edge line should have positive length" diff --git a/tests/test_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py b/tests/test_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py new file mode 100644 index 00000000..cf89e8e4 --- /dev/null +++ b/tests/test_straight_skeleton_2_interior_straight_skeleton_offset_polygon.py @@ -0,0 +1,35 @@ +from compas_cgal.straight_skeleton_2 import offset_polygon, offset_polygon_with_holes + + +def test_offset_polygon_with_holes(): + 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), + ] + + offset = 1.5 + + inner = offset_polygon(points, offset) + outer = offset_polygon(points, -offset) + + assert len(inner) > 0, "Expected at least one inner offset polygon" + assert len(outer) > 0, "Expected at least one outer offset polygon" + + result = offset_polygon_with_holes(outer[0], inner, -0.1) + + assert isinstance(result, list) + assert all(isinstance(item, tuple) and len(item) == 2 for item in result), "Each item should be a (boundary, holes) tuple" + + for boundary, holes in result: + assert hasattr(boundary, "__iter__") + assert isinstance(holes, list) + for hole in holes: + assert hasattr(hole, "__iter__") diff --git a/tests/test_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py b/tests/test_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py new file mode 100644 index 00000000..a85157c3 --- /dev/null +++ b/tests/test_straight_skeleton_2_interior_straight_skeleton_weighted_offset_polygon.py @@ -0,0 +1,30 @@ +from compas_cgal.straight_skeleton_2 import weighted_offset_polygon + + +def test_weighted_offset_polygon(): + 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), + ] + distances = [0.1, 0.3, 0.6, 0.1, 0.7, 0.5, 0.2, 0.4, 0.8, 0.2] + weights = [1.0 / d for d in distances] + offset = 1.0 + + # Call the function + offset_polygons_outer = weighted_offset_polygon(points, -offset, weights) + + # Assertions + assert isinstance(offset_polygons_outer, list), "Expected a list of offset polygons" + assert len(offset_polygons_outer) > 0, "Expected at least one offset polygon" + + for poly in offset_polygons_outer: + assert hasattr(poly, "__iter__"), "Each polygon should be iterable" + assert len(poly) > 2, "Each polygon should have at least 3 vertices" diff --git a/tests/test_straight_skeleton_2_interior_straight_skeleton_with_holes.py b/tests/test_straight_skeleton_2_interior_straight_skeleton_with_holes.py new file mode 100644 index 00000000..ec060405 --- /dev/null +++ b/tests/test_straight_skeleton_2_interior_straight_skeleton_with_holes.py @@ -0,0 +1,40 @@ +from compas.geometry import Polygon +from compas_cgal.straight_skeleton_2 import interior_straight_skeleton_with_holes + + +def test_interior_straight_skeleton_with_holes(): + # Outer boundary + points_boundary = [ + (-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), + ] + boundary = Polygon(points_boundary) + + # Inner holes + holes = [ + [(0.42, 0.88, 0.0), (1.1, -1.0, 0.0), (-1.97, -0.93, 0.0), (-1.25, 1.82, 0.0)], + [(4.25, -0.64, 0.0), (2.9, -3.03, 0.0), (2.12, -2.16, 0.0), (2.89, -0.36, 0.0)], + [(10.6, 0.29, 0.0), (9.48, -1.54, 0.0), (5.48, -1.26, 0.0), (5.98, -0.04, 0.0)], + ] + holes = [Polygon(hole) for hole in holes] + + # Run the function + graph = interior_straight_skeleton_with_holes(boundary, holes) + + # Assertions + assert graph is not None, "Expected a graph object" + assert graph.number_of_edges() > 0, "Graph should have at least one edge" + assert graph.number_of_nodes() > 0, "Graph should have at least one node" + + for u, v in graph.edges(): + line = graph.edge_line((u, v)) + assert line, f"Edge {u}-{v} should have a line geometry" + assert hasattr(line, "length"), "Line object should have a length attribute"