mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-29 00:03:17 +00:00
Classify projection edges in SVG elevations
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 #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 #3668. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,14 @@ a text, a tspan { fill: blue !important; text-decoration: underline;}
|
||||
a:hover { cursor: pointer; }
|
||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
|
||||
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
/* SVG edge classification (issue #3668): see edge-classification.md. These select directly on
|
||||
the <path> element (each classified projection edge carries its own class), so they win over
|
||||
the inherited .projection rule above regardless of specificity. */
|
||||
path.outline { stroke: black; stroke-width: 0.35; stroke-opacity: 1; }
|
||||
path.boundary { stroke: black; stroke-width: 0.3; stroke-opacity: 0.9; }
|
||||
path.sharp { stroke: black; stroke-width: 0.25; stroke-opacity: 0.85; }
|
||||
path.crease { stroke: black; stroke-width: 0.18; stroke-opacity: 0.7; }
|
||||
path.flush { stroke: black; stroke-width: 0.1; stroke-opacity: 0.4; }
|
||||
.surface { stroke: none; fill: #fff; fill-rule: evenodd; }
|
||||
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
|
||||
@@ -261,11 +261,36 @@ class CreateDrawing(bpy.types.Operator):
|
||||
description="Could save some time if you're sure IFC and current Blender session are already in sync",
|
||||
default=True,
|
||||
)
|
||||
svg_ridge_angle_min_deg: bpy.props.FloatProperty(
|
||||
name="Ridge Angle Minimum",
|
||||
description="Minimum convex dihedral deviation from flat, in degrees, for a projection "
|
||||
"edge to be classified as 'sharp' rather than 'flush'. See edge-classification.md",
|
||||
default=45.0,
|
||||
min=0.0,
|
||||
max=180.0,
|
||||
)
|
||||
svg_valley_angle_min_deg: bpy.props.FloatProperty(
|
||||
name="Valley Angle Minimum",
|
||||
description="Minimum concave dihedral deviation from flat, in degrees, for a projection "
|
||||
"edge to be classified as 'crease' rather than 'flush'. See edge-classification.md",
|
||||
default=12.0,
|
||||
min=0.0,
|
||||
max=180.0,
|
||||
)
|
||||
svg_emit_flush_edges: bpy.props.BoolProperty(
|
||||
name="Emit Flush Edges",
|
||||
description="Include projection edges whose dihedral deviation is below both the ridge "
|
||||
"and valley thresholds (class 'flush'). Omitted by default",
|
||||
default=False,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
print_all: bool
|
||||
open_viewer: bool
|
||||
sync: bool
|
||||
svg_ridge_angle_min_deg: float
|
||||
svg_valley_angle_min_deg: float
|
||||
svg_emit_flush_edges: bool
|
||||
|
||||
drawing_name: str
|
||||
is_manifold_cache: dict[str, bool]
|
||||
@@ -1309,6 +1334,14 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.svg_settings = ifcopenshell.geom.settings()
|
||||
self.svg_settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
self.svg_settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.NATIVE)
|
||||
# SVG edge classification (issue #3668). See edge-classification.md.
|
||||
try:
|
||||
self.svg_settings.set("svg-ridge-angle-min-degrees", self.svg_ridge_angle_min_deg)
|
||||
self.svg_settings.set("svg-valley-angle-min-degrees", self.svg_valley_angle_min_deg)
|
||||
self.svg_settings.set("svg-emit-flush-edges", self.svg_emit_flush_edges)
|
||||
except Exception:
|
||||
# Backwards compatibility with older ifcopenshell builds that don't expose these keys.
|
||||
pass
|
||||
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
||||
self.serialiser_settings = ifcopenshell.geom.serializer_settings()
|
||||
self.serialiser = ifcopenshell.geom.serializers.svg(
|
||||
|
||||
Reference in New Issue
Block a user