-
Notifications
You must be signed in to change notification settings - Fork 8
Align libigl barycentric coordinates to compas. #39
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
base: main
Are you sure you want to change the base?
Conversation
|
Hi @petrasvestartas , thank you very much for the work! I found several problems here:
|
|
@petrasvestartas @yiqiaowang-arch what is the status here? |
Hi, as of now I am still using the old definition of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR addresses bug #34 by aligning libigl's barycentric coordinate system with COMPAS conventions. The changes introduce conversion logic to transform libigl's barycentric coordinates (1-u-v)*v0 + u*v1 + v*v2 to COMPAS's format, and modify the intersection functions to return points along with barycentric coordinates instead of just distance values.
Key Changes:
- Added barycentric coordinate conversion from libigl to COMPAS format
- Modified intersection functions to return
(point, face_index, u, v, w)instead of(face_index, u, v, distance) - Added helper function
barycenter_to_pointfor point reconstruction from barycentric coordinates
Reviewed changes
Copilot reviewed 5 out of 7 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/compas_libigl/intersections.py | Core changes: added _conversion_libigl_to_compas and barycenter_to_point functions, updated intersection_ray_mesh and intersection_rays_mesh to return points and COMPAS-aligned barycentric coordinates |
| docs/examples/example_intersections_barycentric.py | New example demonstrating barycentric coordinate usage and comparison with COMPAS |
| docs/examples/example_intersections.py | Updated to use new API returning points directly |
| docs/examples/example_mapping_patterns.py | Code formatting improvements (spacing around operators) |
| docs/examples/example_mapping.py | Code formatting improvements (spacing around operators, trailing whitespace) |
| pyproject.toml | Formatting-only change (newline at end of file) |
| CHANGELOG.md | Documents the bug fix and API change |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| u : float | ||
| The u coordinate (weight for p2) | ||
| v : float | ||
| The v coordinate (weight for p3) | ||
| w : float | ||
| The w coordinate (weight for p1) |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable names in the parameter documentation are inconsistent. The parameters are named u, v, w but the documentation describes them with different semantics. According to the formula P = w*p1 + u*p2 + v*p3, the parameter names should better reflect the vertex they weight (e.g., u_p2, v_p3, w_p1) or the documentation should clarify that despite the generic names, u weights p2, v weights p3, and w weights p1.
| # To match COMPAS barycentric coordinates exactly: | ||
| # COMPAS expects coordinates in order [p1_weight, p2_weight, p3_weight] | ||
| # Our formula is P = w*p1 + u*p2 + v*p3, so COMPAS order should be [w, u, v] | ||
| hits_compas.append([point, idx, u, v, w]) |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The barycentric coordinate ordering is incorrect. The conversion computes the correct coordinate values (w=1-u_libigl-v_libigl for p1, u=u_libigl for p2, v=v_libigl for p3), and the formula P = w*p1 + u*p2 + v*p3 is correct. However, the returned tuple [point, idx, u, v, w] returns coordinates in order (u, v, w) which corresponds to weights for (p2, p3, p1).
According to the documentation claiming compatibility with compas.geometry.barycentric_coordinates, the order should be (p1_weight, p2_weight, p3_weight), which is (w, u, v). The return statement should be hits_compas.append([point, idx, w, u, v]) to match COMPAS convention.
| hits_compas.append([point, idx, u, v, w]) | |
| hits_compas.append([point, idx, w, u, v]) |
|
|
||
| intersections = [] | ||
| for hit in hits_per_ray: | ||
| point, idx, w, u, v = hit |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a discrepancy in how the barycentric coordinates are unpacked. Line 21 unpacks as point, idx, u, v, w, but line 25 unpacks the same hit as point, idx, w, u, v. This inconsistency suggests confusion about the coordinate ordering and will lead to incorrect results. The unpacking order should be consistent throughout the example.
| point, idx, w, u, v = hit | |
| point, idx, u, v, w = hit |
| for h in hits_per_ray: | ||
| idx, u_libigl, v_libigl, _ = h | ||
| w = 1.0 - u_libigl - v_libigl # libigl's (1-u-v) coefficient | ||
| u = u_libigl # libigl's u coefficient |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on this line. This should be removed for code cleanliness.
| u = u_libigl # libigl's u coefficient | |
| u = u_libigl # libigl's u coefficient |
| face = faces[idx] | ||
| p1, p2, p3 = vertices[face[0]], vertices[face[1]], vertices[face[2]] | ||
| point = barycenter_to_point(u, v, w, p1, p2, p3) | ||
|
|
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on this line. This should be removed for code cleanliness.
| hits_per_ray = _intersections.intersection_ray_mesh(P, D, V, F) | ||
|
|
||
| # Convert libigl barycentric coordinates to COMPAS convention | ||
| hits_compas = _conversion_libigl_to_compas(hits_per_ray, M) | ||
|
|
||
| return hits_compas |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking API change. The original API returned (face_index, u, v, distance), but now returns [point, face_index, u, v, w]. While this change appears intentional to fix the bug, existing code that depends on this API will break. Consider:
- Documenting this as a breaking change more prominently in the CHANGELOG
- Updating all existing tests and examples to use the new API
- Considering a deprecation period or version bump to signal the breaking change to users
| return hits_compas | ||
|
|
||
|
|
||
| def barycenter_to_point(u, v, w, p1, p2, p3): |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The barycenter_to_point function is being imported in example files (line 4 and line 10 of the example files), but it's not clear if this function is intended to be part of the public API. If it is public, it should be documented as such (e.g., via __all__). If it's meant to be private/internal, it should be prefixed with an underscore (e.g., _barycenter_to_point) and the example imports should be reconsidered.
| def _conversion_libigl_to_compas(hits_per_ray, M): | ||
| """Convert libigl barycentric coordinates to COMPAS barycentric coordinates. | ||
| Parameters | ||
| ---------- | ||
| hits_per_ray : list[tuple[int, float, float, float]] | ||
| Tuples of (face_index, u, v, distance) from libigl ray intersection | ||
| M : tuple[list[list[float]], list[list[int]]] | ||
| A mesh represented by a tuple of (vertices, faces) | ||
| where vertices are 3D points and faces are triangles | ||
| Returns | ||
| ------- | ||
| list[tuple[list[float], int, float, float, float]] | ||
| Tuples of (point, face_index, u, v, w) in COMPAS barycentric coordinate ordering | ||
| Note | ||
| ---- | ||
| libigl uses: P = (1-u-v)*v0 + u*v1 + v*v2 | ||
| COMPAS uses: P = u*v0 + v*v1 + w*v2 where u + v + w = 1 | ||
| This function converts libigl coordinates to match COMPAS barycentric coordinate ordering | ||
| """ | ||
| vertices = M[0] | ||
| faces = M[1] | ||
|
|
||
| hits_compas = [] | ||
| for h in hits_per_ray: | ||
| idx, u_libigl, v_libigl, _ = h | ||
| w = 1.0 - u_libigl - v_libigl # libigl's (1-u-v) coefficient | ||
| u = u_libigl # libigl's u coefficient | ||
| v = v_libigl # libigl's v coefficient | ||
|
|
||
| face = faces[idx] | ||
| p1, p2, p3 = vertices[face[0]], vertices[face[1]], vertices[face[2]] | ||
| point = barycenter_to_point(u, v, w, p1, p2, p3) | ||
|
|
||
| # To match COMPAS barycentric coordinates exactly: | ||
| # COMPAS expects coordinates in order [p1_weight, p2_weight, p3_weight] | ||
| # Our formula is P = w*p1 + u*p2 + v*p3, so COMPAS order should be [w, u, v] | ||
| hits_compas.append([point, idx, u, v, w]) | ||
| return hits_compas |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new barycentric coordinate conversion logic lacks test coverage. Given that this PR fixes a coordinate system bug, it's critical to add tests that verify:
- The barycentric coordinates returned match those from
compas.geometry.barycentric_coordinatesfor the same point and triangle - The point reconstruction using the returned barycentric coordinates produces the correct intersection point
- The coordinate ordering is correct (weights should be in order p1, p2, p3 to match COMPAS convention)
These tests would prevent regression of the bug this PR aims to fix.
| - The intersection point P = u*p1 + v*p2 + w*p3 where u + v + w = 1 | ||
| - These coordinates match those returned by compas.geometry.barycentric_coordinates |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation formula is inconsistent with the actual implementation. Line 115 states P = u*p1 + v*p2 + w*p3, but based on the coordinate order being returned [point, idx, u, v, w] where (u, v, w) are supposed to be weights for (p2, p3, p1) based on the conversion formula P = w*p1 + u*p2 + v*p3.
If the return order is corrected to [point, idx, w, u, v] to match COMPAS convention (weights in order p1, p2, p3), then this documentation formula should be updated to P = w*p1 + u*p2 + v*p3 where the first coordinate is w, second is u, and third is v.
| - The intersection point P = u*p1 + v*p2 + w*p3 where u + v + w = 1 | |
| - These coordinates match those returned by compas.geometry.barycentric_coordinates | |
| - The intersection point P = w*p1 + u*p2 + v*p3 where w + u + v = 1 | |
| - The returned tuple is (point, face_index, w, u, v), matching COMPAS barycentric coordinate ordering. |
| from compas_viewer import Viewer | ||
|
|
||
| from compas_libigl.intersections import intersection_rays_mesh | ||
| from compas_libigl.intersections import barycenter_to_point |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'barycenter_to_point' is not used.
| from compas_libigl.intersections import barycenter_to_point |
Fix to the bug described here: #34