| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Post-fix retrospectiveHonest accounting of the process on this one, since it took a few detours before landing on the actual fix. What went well
What didn't go so well / where I could have done better
Where you could have done better (said plainly, since you asked for honesty)
NetThe feature works and is verified two independent ways plus your real-world test, but this PR is explicitly not "done" — draft is the right state for it. The live-Blender verification gap is a test-harness problem (see the investigation note I'll add separately), not a sign the code is unverified; IfcConvert CLI and your own real drawing both exercised the actual compiled classification logic end-to-end. |
Sorry, something went wrong.
Update: bonsai_test harness issue found and fixed, live Blender verification now completeRoot cause of the harness issue mentioned above: Bonsai ships as a Blender 4.2+ extension declaring ~90 bundled wheels (including ifcopenshell-*.whl) in its manifest. Blender reinstalls those wheels from the bundled release whenever the addon transitions disabled→enabled. In the dev-testing profile used for this work, that extension repo starts disabled on every fresh headless launch, so a test script that force-enables it (needed to make import bonsai work at all) was retriggering a fresh wheel install on every single run — silently overwriting any manually-built .so, which is why earlier attempts during development couldn't get a live signal from Blender even though the C++ logic was correct. Fix: enable the extension repo/addon once and persist it with bpy.ops.wm.save_userpref(). After that one-time step the addon stays enabled across future launches with no re-enable code needed, and a manually-copied build survives untouched. With that fixed, re-ran the same headless Blender script end-to-end (create_project → assign IfcWall → add_drawing(target_view="ELEVATION_VIEW") → create_drawing) and the real, Bonsai-generated SVG now shows individual <path class="boundary">/<path class="outline"> tokens surviving merge_linework_and_add_metadata's post-processing — confirming per-path classes make it through Bonsai's actual pipeline end-to-end, not just via IfcConvert CLI. (Only boundary/outline showed up in that particular test, since the geometry was a simple axis-aligned test cube with no dihedral folds to produce sharp/crease — consistent with, and no contradiction of, @sboddy's real-project numbers above.) This was a pre-existing gap in the project's own dev-testing setup, not something introduced by this change, so no code in this PR was affected — just closes out the one verification step that was previously blocked. |
Sorry, something went wrong.
Round 2: fixes from real-world testing@sboddy built a dedicated test scene (icosphere, Suzanne, cylinders/cones at various
The test scene in Blender:
The classified SVG result after this round's fixes (SOUTH ELEVATION):
That testing surfaced four real bugs in classify_edge_from_faces() that don't show up on the Issue 1 — silhouette edges misclassified as sharp on near-edge-on faces (fixed)The old outline test was a bare sign-flip: (d0 < 0.0) != (d1 < 0.0). For regular/symmetric Fix: classify front/back/edge-on with a tolerance band around zero (reusing the file's existing Issue 2 — thresholds had no effect; everything was sharp or crease, never flush (fixed)Root cause: an inverted formula. deviation_deg was computed as 180.0 - angle_between_normals_deg, but angle_between_normals_deg (the angle between the two outward Fix: drop the inversion, deviation_deg is just angle_between_normals_deg. This fix is more impactful than expected, in a good way: nearly all curved-surface facet Issue 3 — a specific non-coplanar edge missing entirely (fixed, as a side effect of #1)The "Progressive Angles" rig's left end-cap edge was silently absent (not misclassified — Issue 4 — folds viewed from behind (looking into an open box) read as sharp, should read crease (not fixed — left open)The "Rotated Box w/Boundary" test object has one face removed; looking through the opening shows What was tried: flip the sign of deviation_deg whenever both adjacent faces test as Why it broke: anything reaching this code has already survived HLR's own visibility This was reverted cleanly, with a comment in the code explaining why the naive fix doesn't work, Generated with the assistance of an AI coding tool. |
Sorry, something went wrong.
Sorry, something went wrong.
Update: Issue 4 is now fixed tooFollowing up on the previous comment, which left "Rotated Box w/Boundary" (folds seen from What was previously believed: that bucket-reassigning an edge from sharp to crease could What's actually true, on re-tracing the pipeline: classification happens entirely pre-HLR, The real cause of the previous corruption: an asymmetric-threshold artifact. This project's The fix: gate the flip so it can only reinterpret a fold that would already have been visible Verified against the full test scene (per-object comparison via named <g ifc:name=...> All four issues raised against the real-world test scene are now fixed. Generated with the assistance of an AI coding tool. |
Sorry, something went wrong.
Update: fixed missing silhouette on circular-profile columns/pilesFound while testing against a real model: IfcColumn/IfcPile elements with a circular Root cause: a circular profile produces a genuine analytic cylindrical TopoDS_Face (via Fix: added a face-level pass alongside the existing edge loop — any face whose surface isn't Verified against a real test scene with 3 columns + 1 pile:
This lines up exactly with the report: only the two objects with real circular profiles were Generated with the assistance of an AI coding tool. |
Sorry, something went wrong.
Update: settings now exposed in the Bonsai UIFollowing up on the previous rounds, this adds the interface plumbing requested: a master on/off What's new
The master toggle defaults off, so existing drawings and files are unaffected until a user A bug found (and fixed) along the way — corrected from an earlier overstatementWhile wiring the master toggle through, live-testing in Blender (not just the IfcConvert CLI, I initially described this as making classification "non-functional through Bonsai," which was The genuinely critical impact is on this round's new master toggle specifically: its constructor Verification
Generated with the assistance of an AI coding tool. |
Sorry, something went wrong.
Final review: lessons learned across this PR@sboddy asked for a closing retrospective now that manual testing confirms everything working, in What worked well
Mistakes made, and what they cost
NetFour real-world rendering bugs fixed and verified against a dedicated stress-test scene, one Generated with the assistance of an AI coding tool. |
Sorry, something went wrong.
|
A final comment from the responsible meatbag. I've done a good round of testing on a real model, exercising the options and looking for any outstanding issues. Nothing found so far. It is worth highlighting that this will require the ifcopenshell binary to be bumped in the unstable (and eventually stable) releases. Couple of notes from the meatbag on using AI for such a problem.
|
Sorry, something went wrong.
Adds boundary/outline/sharp/crease/flush classification of HLR projection edges in SvgSerializer, so CSS can style silhouettes, ridges, and valleys differently instead of drawing every edge identically (fixes the "ugly faceted sphere" problem from IfcOpenShell#3668). Classification happens pre-HLR on the original solid's real face topology (three prior attempts tried to classify HLR's own output, which carries no face topology at all and can't be correlated back by edge identity). Each class's visible portion is then extracted via HLRBRep_HLRToShape::VCompound(S)/OutLineVCompound(S), the same per-shape filtering mechanism already used for per-product segmentation, applied per class instead. Classes are tagged directly on individual <path> elements so Bonsai's merge_linework_and_add_metadata group-level class rewrite in operator.py never touches them. New settings: svg-ridge-angle-min-degrees, svg-valley-angle-min-degrees, svg-emit-flush-edges (ConversionSettings.h), wired through Bonsai's CreateDrawing operator and exposed via its redo panel. Refs IfcOpenShell#3668. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes three bugs in classify_edge_from_faces() found via real-world testing against a dedicated stress-test scene (icosphere, Suzanne, cylinders/cones at various orientations, a dihedral-angle sweep rig): - The outline (silhouette) test used a bare sign comparison, so a face at or near exactly edge-on to the camera could land on the wrong side of zero and fall through to angle-based classification instead of being drawn as outline. Now uses a tolerance band around zero, matching an equivalent check already used elsewhere in this file. - The signed deviation-from-flat formula was inverted (180 - angle instead of angle), so small, genuinely near-flat facet angles came out with a large computed deviation and always classified as sharp/crease, never flush. This is why thresholds appeared to have no effect. Also replaced the edge/wire-orientation-based convexity sign (unreliable on real BRep topology, verified wrong against a known fully-convex icosphere) with a simpler position-based test. - A specific edge that was previously missing entirely (not just misclassified) reappears correctly as a side effect of the outline fix above; no separate change was needed for it. A fourth issue (folds viewed through an opening, e.g. a box missing a face, should read as crease rather than sharp) was attempted via a back-facing sign flip, but reverted: it broke the fixes above broadly, since "both faces back-facing" isn't a rare look-through-a-hole case once HLR has already filtered to visible edges only. Documented in a code comment for whoever picks this up next. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Re-enable the view-relative sign flip for folds seen through an opening (e.g. a box with a face removed), reverted in the previous commit after it corrupted unrelated geometry. The earlier revert's diagnosis was slightly off: bucket reassignment can't affect HLR's own visibility computation, so the corruption was actually an asymmetric-threshold artifact -- an unconditional flip re-tested small, correctly-flush deviations against the much smaller valley threshold instead of the ridge one. Gating the flip so it only reinterprets folds that already clear their own pre-flip threshold fixes the box case while leaving every other test object's classification unchanged (verified against the full test scene). Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Circular-profile IfcColumn/IfcPile elements produce a genuine analytic cylindrical BRep face (via BRepPrimAPI_MakePrism), not a tessellated facet. The edge classification/extraction pipeline is edge-identity-based end to end, but a smooth surface's silhouette is synthesized by HLR on the fly and has no corresponding pre-existing edge to bucket, so it was silently dropped once any edge in the product had been classified. Add a face-level pass that includes any non-planar face directly in the outline bucket, giving HLR's per-face OutLine reconstruction a face identity to correlate against. Purely additive: diffing the whole test scene's output before and after shows only the two previously-missing tangent lines appear, nothing else changes. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add svg-use-edge-classification (default off, preserving today's linework), svg-render-crease-edges, and svg-render-sharp-edges settings, gating the existing 5-class classification feature so it can be disabled entirely (falling back to the pre-classification whole-shape output) or have individual classes suppressed. Also fixes a bug uncovered while wiring this into Bonsai: ready(), where geometry_settings() actually gets read into the serializer, was only ever invoked explicitly by IfcConvert's CLI driver and isn't exposed to Python. Every Svg* setting -- including the three from previous rounds -- silently stayed at its hardcoded constructor default when the serializer was constructed directly through the Python bindings, as Bonsai does. Fixed by calling ready() from SvgSerializer's own constructor, safe since it only reads geometry_settings() with no other side effects, and settings are always finalized before construction in every call path. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add UseEdgeClassification, RenderCreases, ValleyAngleMinDegrees, RenderSharp, RidgeAngleMinDegrees, and RenderFlush to EPset_Drawing, following the existing HasUnderlay/DPI/PerspectiveShiftX pattern. The master toggle defaults off, preserving current linework output; the three dependent controls only show in the panel once it's on. Removes the previous dormant, transient operator-redo properties for the ridge/valley thresholds and flush-edge toggle, which were never persisted per-drawing or exposed in any panel, replacing them with the persistent camera properties read in setup_serialiser(). Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The crease and sharp weighting seemed flipped to my sensibilities, so now crease is heavier than sharp. I also added a commented out block for debug colours in case someone wants to quickly use bright colours to diagnose future problems.
|
This is honestly quite nice and self-contained (conceptually at least) on the C++ side. I don't mind merging this in. But I was also already speaking to @Moult about this. The long term plan is:
What you built here in SvgSerializer.cpp: TopExp::MapShapesAndAncestors(*compound_to_hlr, TopAbs_EDGE, TopAbs_FACE, edge_face_map); is essentially this dual graph (faces being the nodes) I mentioned at point 1. This polyhedral triangulation output [0] can also be used so that faces are not necessary triangles. [0] IfcOpenShell/src/ifcgeom/kernels/opencascade/OpenCascadeConversionResult.cpp Lines 106 to 107 in 7b613a0 So this is good and approved on my end for v0.8, but think about the next steps v0.9 (ifcviewer-wgpu is most ahead I think) for a custom polyhedron-based lineworks renderer. |
Sorry, something went wrong.
|
@aothms I just realised in my eagerness to merge I forgot the issue of needing a bump in the binary version. As a result it's broken a few (new) CI tests, which is kind of an unavoidable chicken and egg situation. I'm not even sure I have the rights to initiate a new build at builds.ifcopenshell.org. Could you take a look and bump if OK? |
Sorry, something went wrong.
|
Yes this happens to me all the time. Just try except setting the settings on the python side with a note on when we can remove the try-except. I'll start a build but it will take a while for sure until it trickles down. |
Sorry, something went wrong.
|
I just tested, no harm, no foul. The drawing works even with the settings all active - no errors, or exceptions. It just doesn't do the fancy rendering, so it can just wait for the build to complete and get incorporated. I thought it'd be a problem, but it doesn't seem so. Strange to be honest... I was expecting fireworks. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
Classifies HLR projection edges (elevations/axonometric views, SvgSerializer's
OpenCASCADE Linework mode) into five classes — boundary, outline, sharp, crease,
flush — so CSS can style silhouettes, ridges, and valleys differently instead of drawing
every edge identically. This is the feature requested in #3668: without it, curved/faceted
geometry (e.g. domes, cylinders approximated as many small planar facets) renders every facet
boundary as an identical hard line, which looks wrong compared to a true curved silhouette.
Refs #3668.
Three earlier attempts (see branches feature-svg-edge-classification-3668, -2, -3) got
stuck on what looked like a Python problem but was actually two separate, more fundamental
issues — see the attached plan document for the full root-cause writeup. Short version:
attribute wholesale on every projection <g> it visits — but only at the group level.
This PR tags classes directly on individual <path> elements instead, which that function
never touches, sidestepping the problem entirely rather than patching around it.
edge-only with no face topology, so any attempt to classify it after HLR runs (by looking
up face-adjacency for its output edges) can never work, regardless of identity-matching
fixes. The fix classifies edges before HLR (on the original solid, which has real
faces), then extracts each class's visible portion via
HLRBRep_HLRToShape::VCompound(S)/OutLineVCompound(S) — the same per-shape filtering
mechanism already used in this file for per-product segmentation, repurposed per class.
What's verified
outline/sharp classes on individual <path> elements (previously either 100%
unclassified or 100% one bucket, depending on the attempt).
convex/concave face pairs, confirming the sign convention (convex → sharp, concave →
crease) is correct.
copied into a live Bonsai profile): 798 outline, 131 crease, 1291 sharp edges produced.
Confirms the fix works end-to-end through Bonsai's actual pipeline including
merge_linework_and_add_metadata, not just via the CLI.
What's not done / known issues
— colours picked in default.css were placeholders for testing, not a final visual design.
The real-world test above rendered blue lines because the CSS wasn't part of that manual
copy — expected, not a bug, but worth a proper design pass before this is considered finished.
been run through the project's own automated Blender test harness (see follow-up comment —
that harness has an unrelated pre-existing issue that blocked this during development).
before this is ready to come out of draft.
HLR/projection path used for elevations.
omitting them) is deferred to a future issue.
AI disclosure
Per this repo's AGENTS.md: this change (research, design, implementation, and the attached
planning artifacts) was produced by Claude (Anthropic) working with @sboddy across an extended
session — investigating the prior failed attempts, confirming/refining the root-cause
hypothesis, designing and implementing the fix, and independently verifying it. See the
follow-up comment on this PR for an honest retrospective on the process.
Attached: initial task prompt given to the AI agent