Remove DrawingCtx.drawsub_stack
The DrawingCtx.drawsub_stack
field is only used to implement the behavior of render_layer
/geometry_for_layer
. Those take a document, find one of the "user addressable" elements, and render only that subtree exactly as it would be if the whole document were rendered - thus the "layer" terminology, as in an illustration program. I'll call the element that corresponds to the user-supplied id
the "layer" in the following discussion.
The trick is "as it would be if the whole document were rendered": doing this operation involves reconstructing the layer's viewport, transform, and cascade as if it were rendered as part of the whole document. The recursive nature of DrawingCtx.draw_node_from_stack
-> Node.draw
-> Node.draw_children
, plus the individual element implementations of draw
, do this reconstruction implicitly.
The only place where the drawsub_stack
is used is in DrawingCtx::draw_node_from_stack
:
- The start of the drawing process is
draw_tree
; it creates a list of ancestors of the layer, so thedrawsub_stack
ends up beingVec<Node>
likeroot, ..., ..., layer
. -
draw_node_from_stack
essentially implements this: if there is a non-empty stack it skips drawing of nodes that are not in the stack, otherwise it draws everything.
In principle one could build the list of ancestors, iterate it while accumulating the (viewport, transform, cascade) state, and just draw the layer directly. In practice this is complicated because the accumulation of those things is done implicitly in the recursive drawing code.
A little braindump of ideas:
- Above I mentioned a "user addressable" element as a layer. This means that all the ancestors of the layer are elements that can draw themselves. For example, the path in
<svg> <g> <g> <path id="layer"/>
is user addressable, but<svg> <defs> <g> <path id="layer"/>
is not, since the<defs>
is not drawable. The code does not check this condition; it just sort of happens implicitly because the Vec of ancestor nodes would hit adefs
, which doesn't draw anything. It may be good to have tests to formalize this behavior. - From a quick scan of the elements, only
svg
andg
can be nested to end up in an addressable element. Of those two, onlysvg
changes the viewport. Both can change thetransform
; both have a simple cascade that will be already computed by the time the drawing is invoked (i.e. they are not like<use>
which needs a special re-cascade). - In !442 (merged) I extracted a
Svg::push_viewport
method so that the layer-drawing code could use it; my refactoring didn't quite get to that state and the method is just called fromSvg::draw
.