Read an SVG's styling and clip paths, and import outline art as colored parts - #12088
Read an SVG's styling and clip paths, and import outline art as colored parts#12088Cstm3DBldr wants to merge 1 commit into
Conversation
e2de62e to
f50e223
Compare
f50e223 to
74f87c0
Compare
…ed parts The SVG parser reads only presentation attributes, so it sees neither the styling a drawing program keeps in a <style> block nor the clip paths that trim the artwork. A file exported with "style elements", which is the default in common drawing programs, therefore arrives with every shape the same default color, and clipped artwork arrives whole with the clip shapes themselves drawn as if they were part of the drawing. prepare_svg() folds both into the elements before parsing: class rules become plain fill and stroke attributes, and the elements a clip applies to are marked so their shapes can be recognised afterwards. The clip shapes are copied into the drawing rather than read on their own, because the parser sizes a document from the bounds of what it contains - read separately a clip path is measured against itself and no longer lines up with what it trims. collect_clip_regions() gathers them once parsed and create_shape_with_ids() trims the shapes each one applies to. This is shared, so importing an SVG through the existing gizmo now keeps the colors the file gives it, honours its clip paths, and no longer draws the clip shapes. On top of that, "Import SVG as color regions" builds a part for every area the artwork is drawn as. Outline art carries its meaning in the areas its lines enclose, which the file never states: only the lines are drawn. Each closed path's enclosed area is taken, overlaps are resolved the way the drawing itself resolves them - a shape keeps only what nothing painted after it covers - and every region is extruded separately, since a merged outline would be triangulated without regard to where one color meets the next. Each region is then assigned the loaded filament nearest the color it was drawn in, and the object is centred on the plate being worked on. Artwork resolves to only as many colors as there are filaments to print it in, far fewer than it has regions, so the import offers to merge the regions of each color into one part - recoloring is then a click per color rather than per region. Those regions are disjoint islands, so merging them is only a concatenation, and Split to objects recovers them. The choice is skipped when every region is already its own color. Colors the project does not have are offered as additions rather than silently collapsed. With one filament loaded a three-color drawing previously imported as a single part, because every region matched the only filament there was - which is precisely the case issue bambulab#8044 describes. The import now compares each color the artwork uses against the loaded filaments and, when some are missing, offers to add them through Sidebar::add_custom_filament(), the same path as the + button; matching what is already loaded stays available and the question is skipped when nothing is missing. Signed-off-by: Cstm3DBldr <34087122+Cstm3DBldr@users.noreply.github.com>
74f87c0 to
e47c6bc
Compare
|
Pushed an update. Adding a color region import turned out to have a hole in it: the regions were The import now compares each color the artwork is drawn in against the loaded Adding goes through Sidebar::add_custom_filament(), the same path as the + button in Also went back over the diff for anything a reviewer should not have to read: a |
Closes #8044.
Two related changes to SVG import: the parser is taught to read styling and clip paths it currently ignores, and on top of that a new import builds a part for every color an outline drawing is made of.
The first half is a fix to SVG import as it stands today. The second is additive and can be dropped if it is not wanted.
Reading what the file says
NanoSVG reads only presentation attributes. It sees neither the styling a drawing program keeps in a
<style>block nor the clip paths that trim the artwork, and it has no notion of<clipPath>at all, so it draws the clip shapes as though they were part of the drawing.For a file exported with "style elements" - the default in common drawing programs - the result is that every shape arrives the same default color, clipped artwork arrives whole and spills past the edge it was meant to stop at, and the clip shapes appear in the model as stray geometry.
prepare_svg()folds both into the elements before parsing:fillandstrokeattributes, leaving alone anything the element already states itself. Several classes on one element are applied in order, the later winning, as CSS does.collect_clip_regions()gathers the clip regions once the document is parsed, andcreate_shape_with_ids()trims the shapes each clip applies to. Both are shared, so importing an SVG through the existing gizmo now keeps the colors the file gives it, honours its clip paths, and no longer draws the clip shapes.Nothing about sizing changes, and a file that carries neither a
<style>block nor a clip path is returned untouched.Before - the same file on current master. Every shape arrives one color, the clip is ignored, and the clip circles are drawn as artwork:
After:
Importing outline art as colored parts
File > Import > Import SVG as color regions
Outline art carries its meaning in the areas its lines enclose, which the file never states - only the lines are drawn. Plain import draws what the file says to draw, so such a file arrives as a set of thin ribbons rather than the shapes a reader sees.
This takes each closed path's enclosed area instead, and:
Colors the project does not have
Matching regions to whatever filaments happen to be loaded falls apart when there are not enough of them: a three-color drawing imported into a project with one filament would arrive as a single part in that color, with nothing on screen to say why. That is the situation #8044 describes, so the import compares the colors the artwork is drawn in against the loaded filaments and offers to add the ones that are missing:
Adding goes through
Sidebar::add_custom_filament(), the same path as the + button in the filament panel, so they are ordinary project filaments carrying the SVG's own RGB. Matching what is already loaded stays available, and the question is skipped when every color the drawing uses is already there - so a project set up with the right filaments imports without being asked anything.Artwork resolves to only as many colors as there are filaments to print it in, which for a drawing of any size is far fewer than it has regions - the logo below is 136 regions in 3 colors. Since handling 136 parts to change 3 colors is nobody's idea of convenient, the import asks:
One part per color merges the regions printing in each filament, so recoloring is a click per color. The regions of a color are disjoint islands, so this is only their meshes concatenated - nothing overlaps and no geometry has to be resolved - and Split to objects brings the individual regions back. The question is skipped when every region is already its own color, there being nothing to choose.
One part per color - the logo arrives as three parts, one per filament:
One part per region - the same import keeping all 136:
Split to objects, recovering the individual regions from a merged color:
It is a separate action rather than a change to SVG import, because the two produce different things: the gizmo makes one editable volume, this makes a part per color. Anyone importing an SVG today gets exactly what they got before.
Testing
Built on Windows and exercised on real artwork - a logo of 211 shapes using CSS classes, two clip paths on groups, and overlapping fills throughout. Results were checked by reading the per-triangle data back out of the saved 3MF rather than by eye:
Plain outline artwork, where the drawing states only the lines and not the areas they enclose:
Sliced, showing the regions resolve to the right filaments:
Notes
Curves are flattened to the same 0.1 mm chord tolerance the SVG gizmo already uses.
Not handled, and left for later:
clipPathUnits="objectBoundingBox", a transform on a clipped group, and CSS selectors other than class rules. Gradients have no single color to print and are skipped, as before.