Skip to content

Add convex_polygon shape; wraps Voronoi cells into the shape pipeline (#114 follow-up) - #60

Merged
petlenz merged 1 commit into
mainfrom
feature/convex-polygon-shape
May 15, 2026
Merged

Add convex_polygon shape; wraps Voronoi cells into the shape pipeline (#114 follow-up)#60
petlenz merged 1 commit into
mainfrom
feature/convex-polygon-shape

Conversation

@petlenz

@petlenz petlenz commented May 15, 2026

Copy link
Copy Markdown
Member

Summary

Adds a 2D convex_polygon shape that bridges voronoi_generator_2d's raw vertex output to the standard rvegen pipeline (writers, generators, intersection dispatch, etc.). After this PR a polycrystal-style RVE is two calls:

```cpp
rvegen::voronoi_generator_2d gen{1.0, 1.0, seeds};
auto cells = gen.build();
std::vector<std::unique_ptr<rvegen::shape_base>> shapes;
for (auto& v : cells) {
shapes.push_back(std::make_unique<rvegen::convex_polygon>(std::move(v)));
}
// shapes now flows through voxel_writer / gmsh_geo_writer / svg_writer.
```

What lands

  • convex_polygon<T> shape type, registered via static_indexing<convex_polygon<T>, shape_base<T>>.
  • Full shape_base contract:
    • area() — shoelace formula (abs of signed area, robust to winding).
    • volume() — 0 (2D shape).
    • is_inside(point) — left-of-every-edge test (O(N), correct for convex polygons only).
    • make_bounding_box() — AABB over vertices, returns rectangle_bounding.
    • get_middle_point() / set_middle_point() — area-weighted centroid; translation moves every vertex.
    • clone() returns an independent copy.

Design notes

  • Convex-only by design. is_inside is the simple "same side of every edge" test, valid only for convex polygons. The motivating input (Voronoi cells) is always convex. A general-polygon shape with ray-cast is_inside would be a separate type.
  • Winding-order convention: CCW. The ctor trusts the caller; voronoi_generator_2d already emits CCW. A CW polygon would pass area() (which uses abs) but make is_inside reject every query — diagnosed easily by a test that constructs an obviously-inside point.
  • Translation is O(N). set_middle_point moves every vertex by the centroid delta. For a typical Voronoi cell with 4–8 vertices this is cheap; future "thousands of vertices" cases would benefit from an offset-vector indirection (cf. mesh_inclusion's pattern from Add mesh_inclusion_input: JSON-driven STL placement (#9 phase 2) #27), but that's premature for current use cases.

Test plan

  • Unit square: area = 1, centroid = (0.5, 0.5), interior point inside, exterior outside, edge point inside.
  • Right triangle: area = 0.5, interior point inside, exterior point outside.
  • set_middle_point({10, 5, 0}) translates the polygon; original interior point now exterior, new centroid interior.
  • AABB matches min/max of vertex coordinates.
  • clone() produces an independent copy (translating the copy doesn't affect the original).
  • < 3 vertices throws.
  • End-to-end Voronoi wrapping: 2-seed Voronoi → 2 convex_polygon shapes, each correctly classifies its seed's neighbourhood.
  • All 15 ctest targets green locally.

Deferred

  • Wrap-helper voronoi_to_shapes(generator) that returns the shape_vector directly — trivial composition but a separate ergonomics call.
  • Python binding for ConvexPolygon.
  • Output-writer support: voxel_writer already works via is_inside; gmsh_geo_writer's write_2d would need a Plane Surface dispatch case to emit the polygon natively (today it skips with (unsupported 2D shape skipped)). That's a separate gmsh-writer extension.

…ementing the full shape_base contract — area (shoelace), is_inside (left-of-every-edge for convex polygons), AABB bounding box, centroid translation. Wraps voronoi_generator_2d's per-cell output so polycrystal RVEs flow through the standard writer/generator pipeline. End-to-end test confirms cells[0] and cells[1] from a 2-seed Voronoi are correctly classified by is_inside
@petlenz
petlenz merged commit 17f5236 into main May 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant