The silhouette detection was using 2D orientation (CCW/CW in XY projection)
which fails for faces perpendicular to the view direction. When a face's
normal is parallel to the Z-axis (truly perpendicular to top-down view),
the 2D cross product becomes zero and the face is neither CCW nor CW.
This fix replaces the 2D orientation approach with 3D normal-based detection:
- Categorize faces by the sign of normal.z:
- normal.z < 0: front-facing (visible from top)
- normal.z > 0: back-facing (not visible)
- normal.z == 0: vertical/edge-on
- An edge is a silhouette if adjacent faces have different categories
This correctly detects silhouette edges for:
- Front-facing + back-facing faces
- Front-facing + vertical faces
- Back-facing + vertical faces
Affects: extract_silhouette_contour_edges() and extract_lineart_edges()
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>