Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 27, 2025

Implementation Complete: Mesh.volume() method

Summary

Successfully implemented a volume() method for the Mesh class that computes the volume of closed meshes using signed volume of tetrahedra. This approach works for both convex and non-convex meshes, with optional parameters for performance optimization.

Implementation Details

  • Understand the signed volume algorithm for triangulated meshes
  • Implement volume() method using signed volume approach (not volume_polyhedron which only works for convex meshes)
  • Return None if mesh is not closed
  • Use signed volume calculation based on triangulated faces
  • Use built-in triangulation from to_vertices_and_faces(triangulated=True)
  • Add optional copy and unify_cycles parameters (both default to True) for performance optimization
  • Add comprehensive tests with exact volume verification
  • Verify implementation with known volumes using Box geometries with non-unit dimensions
  • Test tetrahedron with actual expected volume value
  • Apply code formatting with ruff and black
  • Address all code review feedback
  • Add mesh copy to avoid in-place modifications (optional)
  • Unify face cycles to ensure consistent orientation (optional)
  • Document non-convex face triangulation limitations
  • Update CHANGELOG.md
  • Fix IronPython encoding compatibility
  • Run security checks (passed with 0 alerts)

Key Features

  • Closed mesh detection: Returns None for open meshes
  • Works for non-convex meshes: Uses signed volume approach instead of volume_polyhedron
  • Non-destructive by default: Creates a copy of the mesh to avoid modifying the original (configurable via copy parameter)
  • Consistent orientation by default: Unifies face cycles before computing volume (configurable via unify_cycles parameter)
  • Performance optimization: Optional parameters allow disabling copy and unify_cycles for performance-critical scenarios
  • Built-in triangulation: Uses to_vertices_and_faces(triangulated=True) for clean, maintainable code
  • IronPython compatible: Uses only ASCII characters for compatibility
  • Accurate results: Tested with boxes of various dimensions showing exact volume calculations
  • Documented limitations: Clear documentation about non-convex face triangulation using centroids

Method Signature

def volume(self, copy=True, unify_cycles=True):
    """
    Parameters
    ----------
    copy : bool, optional
        If True, a copy of the mesh is made before computation to avoid modifying the original.
        Default is True.
    unify_cycles : bool, optional
        If True, face cycles are unified to ensure consistent orientation.
        Default is True.
    
    Returns
    -------
    float | None
        The volume of the mesh if the mesh is closed, None otherwise.
    """

Algorithm

The volume is computed using the signed volume of tetrahedra formed by each triangulated face and the origin:

  1. Optionally create a copy of the mesh (controlled by copy parameter)
  2. Optionally unify face cycles (controlled by unify_cycles parameter)
  3. Use built-in triangulation via to_vertices_and_faces(triangulated=True)
  4. For each triangulated face (a, b, c), compute the signed volume: V = (1/6) * (a dot (b cross c))
  5. Sum all signed volumes and return the absolute value

This approach works correctly for both convex and non-convex closed meshes.

Tests

All 119 existing mesh tests pass, plus comprehensive volume tests with exact value verification:

  • Box(3, 4, 5) → volume = 60 (exact)
  • Box(2, 2, 2) → volume = 8 (exact)
  • Box(2, 3, 4) → volume = 24 (exact) with copy=False, unify_cycles=False
  • Tetrahedron from polyhedron → volume = 0.5132 (exact)
  • Sphere approximation → volume ≈ 4.189 (validates accuracy within 1%)
  • Open mesh → returns None

Security Summary

CodeQL analysis completed with 0 security alerts.

Original prompt

This section details on the original issue you should resolve

<issue_title>Volume for mesh</issue_title>
<issue_description>Can we add a volume for Mesh? as a computed property. using signed volume of triangles. we can return a None if mesh is not closed.</issue_description>

Comments on the Issue (you are @copilot in this section)

@Licini @gonzalocasas I was hoping to use the copilot to create a PR for this but seems it need a pro subscription 😂... just curious are you able to kick it off from your side? @tomvanmele there is a `compas_cgal` pluggable that can be plugged in.

there is volume_polyhedron in compas.geometry but this is only valid if the polyhedron/mesh is convex, has planar/triangle faces, and is positively oriented. writing a more general fallback is tricky...</comment_new>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 4 commits October 27, 2025 07:10
Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copilot AI changed the title [WIP] Add computed volume property for mesh using signed triangles Add volume() method to Mesh class Oct 27, 2025
Copilot AI requested a review from Licini October 27, 2025 07:21
@Licini
Copy link
Contributor

Licini commented Oct 27, 2025

@copilot Why not use volume_polyhedron?

Simplified implementation by reusing the existing volume_polyhedron
function from compas.geometry instead of reimplementing the algorithm.
This reduces code duplication and improves maintainability.

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot Why not use volume_polyhedron?

Good catch! I've refactored the implementation to use volume_polyhedron from compas.geometry instead of reimplementing the algorithm. This simplifies the code significantly (from ~30 lines to 2 lines) and reuses existing, well-tested functionality. The mesh's to_vertices_and_faces() method provides the exact format needed by volume_polyhedron.

Fixed in commit 504be2a

@Licini
Copy link
Contributor

Licini commented Oct 27, 2025

@copilot besides checking if mesh is closed you should also check if the face cycles are unified (unify it if not), also if volume_polyhedron does not triangulate the mesh, you need to do it first, make sure you don't modify it in place when you do these, make a copy

- Create a copy of the mesh to avoid in-place modifications
- Call unify_cycles() on the copy to ensure consistent face orientation
- Deep copy vertices list since volume_polyhedron modifies it in place
- Updated documentation to reflect internal operations

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot besides checking if mesh is closed you should also check if the face cycles are unified (unify it if not), also if volume_polyhedron does not triangulate the mesh, you need to do it first, ma...

Done! The implementation now:

  1. Creates a copy of the mesh using .copy() to avoid modifying the original
  2. Unifies face cycles by calling .unify_cycles() on the copy to ensure consistent orientation
  3. Deep copies the vertices list (vertices = [v[:] for v in vertices]) since volume_polyhedron modifies it in place when triangulating non-triangular faces

All tests pass and the original mesh remains unmodified.

Fixed in commit c88b6e8

@codecov
Copy link

codecov bot commented Oct 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.24%. Comparing base (587ff27) to head (84dbabb).
⚠️ Report is 15 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1497      +/-   ##
==========================================
+ Coverage   62.21%   62.24%   +0.02%     
==========================================
  Files         208      208              
  Lines       22437    22452      +15     
==========================================
+ Hits        13960    13976      +16     
+ Misses       8477     8476       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Licini
Copy link
Contributor

Licini commented Oct 28, 2025

@copilot actually, volume_polyhedron might not be the best choice, because it only works for convex mesh. Double check if this is the case, if true, use signed triangles instead.
And update changelog.md

…support

- volume_polyhedron only works for convex meshes (as per its documentation)
- Implemented signed volume of tetrahedra approach which works for both convex and non-convex meshes
- Removed volume_polyhedron import, added dot_vectors import
- Updated CHANGELOG.md to document the new volume() method
- All tests pass with same accuracy

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 28, 2025

@copilot actually, volume_polyhedron might not be the best choice, because it only works for convex mesh. Double check if this is the case, if true, use signed triangles instead.
And update changelo...

Correct! I've verified that volume_polyhedron only works for convex meshes (as stated in its documentation). I've replaced it with a signed volume of tetrahedra approach that works for both convex and non-convex meshes.

The implementation now:

  1. Uses signed volume calculation: for each triangulated face, computes V = (1/6) * (a · (b × c))
  2. Works correctly for both convex and non-convex closed meshes
  3. Updated CHANGELOG.md with the new volume() method

All tests pass with the same accuracy.

Fixed in commit 6e1bc67

@Licini
Copy link
Contributor

Licini commented Oct 28, 2025

@copilot use the builtin triangulation

- Replaced manual fan triangulation with to_vertices_and_faces(triangulated=True)
- Cleaner and more maintainable code
- Uses the mesh's built-in triangulation method
- All tests pass with same accuracy

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 28, 2025

@copilot use the builtin triangulation

Done! Replaced manual fan triangulation with the built-in to_vertices_and_faces(triangulated=True) method. This is cleaner, more maintainable, and uses the mesh's existing triangulation functionality. All tests pass with the same accuracy.

Fixed in commit 066c0f5

@Licini Licini marked this pull request as ready for review October 28, 2025 07:24
@Licini
Copy link
Contributor

Licini commented Oct 28, 2025

@gonzalocasas @tomvanmele took few iteration, how does this look guys?

We check is_closed first then do unify cycle and triangulation, it uses signed tetrahedron volume, so it works for non-convex mesh too, we take abs at the end so it doesn't matter if unified faces pointing outwards or not. The only thing that are not considered is non-manifold and self-intersection.

@Licini
Copy link
Contributor

Licini commented Oct 28, 2025

@copilot fix the encoding error in ironpython test:

Run ipy tests/ipy_test_runner.py
============================== test session starts ==============================
tests\compas\test_iotools.py .............
  File "D:\a\compas\compas\src\compas\datastructures\mesh\mesh.py", line 3931

SyntaxError: Non-ASCII character '\xc2' in file D:\a\compas\compas\src\compas\datastructures\mesh\mesh.py on line 3931, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

tests\compas\test_itertools.py

Replace non-ASCII characters (· and ×) with ASCII equivalents (dot and cross)
in comment on line 3931 to fix IronPython syntax error.

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 28, 2025

@copilot fix the encoding error in ironpython test:

Run ipy tests/ipy_test_runner.py

...

Fixed! Replaced non-ASCII characters (· and ×) with ASCII equivalents ("dot" and "cross") in the comment on line 3931. The file now uses only ASCII characters and should work with IronPython.

The issue was caused by UTF-8 encoded mathematical symbols that IronPython couldn't parse.

Fixed in commit 856f48e

Copy link
Member

@gonzalocasas gonzalocasas left a comment

Choose a reason for hiding this comment

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

generall lgtm, but I think testing for volume is not None is not really enough, only the sphere is tested properly, the cube from stl relies on data that is not on the test itself, not a big issue, but for a simple cube test, it would be maybe easier to build one in the test method, use a dimension that is not 1 to make sure things are really working properly, and then test for the expected volume

@tomvanmele
Copy link
Member

tomvanmele commented Oct 29, 2025

looks good, but in addition to @gonzalocasas comments, note that in moderately complex cases, calls will be expensive. perhaps copy and unify cycles should be made optional (or optionally turned off, since copy and to vertices and faces actually make two copies which might not be necessary in many cases).

also note that when faces are non-convex, the triangulation might not be correct, since it uses the centroid of the face...

@Licini
Copy link
Contributor

Licini commented Oct 29, 2025

@copilot considering above comments, do this:

  1. use compas box to build cube instead of loading stl, using a dimension that is not 1 .
  2. all tests should to check with actual number making sure the math is right.
  3. Make copy and unifying cycle optional (both default to true)
  4. add a note in doc string explaning that when faces are non-convex, the triangulation might not be correct, since it uses the centroid of the face.

1. Added copy and unify_cycles parameters (both default to True) for performance optimization
2. Updated tests to use Box instead of STL files with non-unit dimensions (3x4x5, 2x2x2, 2x3x4)
3. All tests now check actual expected volumes with proper math verification
4. Added docstring note about non-convex face triangulation limitations
5. Added test for optional parameters (copy=False, unify_cycles=False)

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 29, 2025

@copilot considering above comments, do this:

  1. use compas box to build cube instead of loading stl, using a dimension that is not 1 .
  2. all tests should to check with actual number making sure th...

Done! All four points addressed:

  1. ✅ Tests now use Box instead of STL files with non-unit dimensions (3x4x5, 2x2x2, 2x3x4)
  2. ✅ All tests check actual expected volumes: Box(3,4,5)=60, Box(2,2,2)=8, Box(2,3,4)=24, Sphere≈4.189
  3. ✅ Added copy and unify_cycles optional parameters (both default to True):
    def volume(self, copy=True, unify_cycles=True):
  4. ✅ Added docstring note: "When faces are non-convex, the triangulation might not be correct, since it uses the centroid of the face. For accurate results with non-convex faces, consider using a mesh with triangulated faces."

Fixed in commit d7e80c8

Copilot AI changed the title Add volume() method to Mesh class Add volume() method to Mesh class with optional performance parameters Oct 29, 2025
tet = Mesh.from_polyhedron(4)
volume = tet.volume()
assert volume is not None
assert volume > 0
Copy link
Contributor

Choose a reason for hiding this comment

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

use actual volume number not just check >0 @copilot !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Updated the tetrahedron test to check the actual expected volume of 0.5132002392796675 instead of just checking > 0. The test now verifies the exact volume for the platonic tetrahedron from polyhedron(4).

Fixed in commit 84dbabb

Changed tetrahedron test from checking > 0 to verifying the actual expected
volume of 0.5132002392796675 for the platonic tetrahedron from polyhedron(4).

Co-authored-by: Licini <17893605+Licini@users.noreply.github.com>
Copilot AI requested a review from Licini October 29, 2025 15:44
@Licini
Copy link
Contributor

Licini commented Oct 29, 2025

@tomvanmele @gonzalocasas adjusted based on your guys comment, shall i merge now?

@gonzalocasas
Copy link
Member

@Licini I would say so, however, I'd wait for @tomvanmele feedback because he mentioned today that the first implementation that copilot suggested for volume might actually be more accurate than the current one

@tomvanmele
Copy link
Member

we can merge this as-is and then open a PR to improve the triangulation algorithm...

@Licini Licini merged commit c0d480d into main Oct 30, 2025
33 checks 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.

Volume for mesh

4 participants