Skip to content

docs: document drawing stylesheets and the override pattern - #9358

Open
BIMvoice wants to merge 1 commit into
IfcOpenShell:v0.8.0from
BIMvoice:docs-drawing-stylesheet-guide
Open

BIMvoice wants to merge 1 commit into
IfcOpenShell:v0.8.0from
BIMvoice:docs-drawing-stylesheet-guide

Conversation

@BIMvoice

Copy link
Copy Markdown
Contributor

Bonsai styles its SVG drawings with CSS, and the system is genuinely capable. default.css covers cut versus projection, edge classification, named line weights, every annotation predefined type, text sizes and material hatches.

But there is currently no documentation for any of it. Searching src/bonsai/docs/ for "stylesheet" returns nothing. So a BIM coordinator who wants heavier walls in plan has to discover the file exists, find it, learn CSS, and guess that the selector is .cut.

This adds a guide written for that person rather than for a developer.

The thing most worth documenting

svgwriter.py's add_stylesheet() splits the path on commas and appends each file in order:

path_list = [p.strip() for p in paths.split(",")]
for path in path_list:
    ...
    self.svg.defs.add(self.svg.style(stylesheet.read()))

So normal CSS cascade applies and later files win. Nobody needs to edit default.css. They set the path to default.css, my-office.css and write three lines of their own. That reduces "learn CSS and edit a 103 line file" to "add two lines", and as far as I can tell it has never been written down anywhere.

Verified by running the real cascade logic against two files and confirming in rendered output that the second file's rule wins on a shared selector while untouched properties still inherit from the first.

Also documented, because it costs people an afternoon

core/drawing.py:335 bakes the resolved path onto each drawing's EPset_Drawing at creation time:

"Stylesheet": drawing.get_default_drawing_resource_path("Stylesheet"),

Changing the project pset or the add-on preference therefore affects future drawings only. An existing drawing keeps the path it was created with and has to be edited directly. That is reasonable behaviour but surprising, and it is the first thing someone hits when they change the setting and nothing happens.

What is in the guide

Where the setting lives, tracing both the BBIM_Documentation / StylesheetPath pset on IfcProject and the add-on preference fallback. The override pattern, front and centre. A class reference generated from default.css and cross-checked against what the code actually emits. A units section, since stroke widths are millimetres while font sizes are px with mm equivalents in comments. Six worked recipes for common requests. Troubleshooting, including that a mistyped class fails silently.

Plus three short starter override files, ten to twenty lines each, override-only rather than copies of the default.

Findings along the way, reported rather than fixed

Nothing here is changed by this PR. Flagging them because they turned up while cross-checking the reference against the source.

.material-blank never matches. The code emits material-null (bim/module/drawing/operator.py:1460) and default.css has no rule for that class, so elements without a material fall through unstyled. Confirmed by grep: zero occurrences of material-null in the CSS.

Material class names are case sensitive. canonicalise_class_name (tool/drawing.py:170) only strips non-alphanumerics, so a material named "Concrete" yields material-Concrete and silently misses the material-concrete hatch rule. This is documented in the troubleshooting section as something to watch for, since it is a real trap regardless of whether the code changes.

Five selectors are dead. .PredefinedType-STUD, WOOD, STEEL, CONCRETE and PLASTERBOARD are not emitted anywhere in the Python or the C++.

sample.css looks orphaned. It is referenced nowhere and uses a stale vocabulary (.hidden, .solid, .leader) that does not match current selectors.

One thing I checked and want to correct in advance, in case it looks like a bug: .PredefinedType-RADIUS points at #fall-marker-end and .PredefinedType-FALL at #radius-marker-end, which reads as a swap. It has no visible effect, because both markers in markers.svg have byte-identical geometry (M 0 3.5 L 0 10.5 L 10 7). Untidy, not broken, so I have left it alone.

Verification and its limits

The cascade was verified by executing the real add_stylesheet() logic and rendering the result, not by reading the code. The RST parses cleanly under docutils.

Not verified: end to end drawing generation inside Blender. The cascade behaviour was proven directly, but nobody has produced a real drawing with an override file through the full Bonsai pipeline. Worth someone doing before this is relied on.

Produced with AI assistance.

…files

Bonsai's drawing CSS (default.css) and its comma-separated, cascading
stylesheet setting were undocumented. Add a guide covering where the
setting lives (project pset vs add-on preferences, and why it only
affects new drawings), the override-instead-of-edit pattern, a class
reference generated from default.css and cross-checked against what
svgwriter.py/SvgSerializer.cpp actually emit, unit conversions, worked
recipes and troubleshooting. Add three short starter override files
(heavy lineweight, fine lineweight, presentation greys) referenced from
the guide.
- A minor edge, a small but real change in direction.
* - ``path.flush``
- An edge between two faces that are nearly coplanar. Drawn
lightest, often barely visible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A technically correct explanation of what these classifications are:

  • path.outline Partially correct. When there are two faces sharing an edge, one facing the camera, and the other edge-on or facing away. So it marks the silhouette, but also contours.
  • path.boundary Wrong. A boundary is where an edge only has one face. It is where an object is not a closed, manifold mesh.
  • path.crease Wrong. When the two faces sharing an edge form a valley with a great enough angle.
  • path.sharp Wrong. When the two faces sharing an edge form a ridge with a great enough angle.
  • path.flush Wrong. When the two faces are not coplanar, but do not pass the threshold to make them a crease (valley) or sharp (ridge) they are set to flush.

A useful document to add though! 👍️

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.

2 participants