meerk40t / svgelements

SVG Parsing for Elements, Paths, and other SVG Objects.
MIT License
130 stars 29 forks source link

SVGElements needs some functionality for Clippath #74

Closed tatarize closed 3 years ago

tatarize commented 3 years ago

These are way more common than I ever gave the credit for. It seems like Clippath would be a kind of svg element in it's own right. It might need some indexing within the shadow dom to process the URL() data.

abey79 commented 3 years ago

Yeah, cairo is often used for python gen art. Since it generates clipping, it's a decent argument in favour of supporting it.

Proper parsing support would be great. I can't see actual clipping being done without a dependancy such as Shapely/PyGEOS though, but that's something the client code could handle (although I'm not sure if GEOS knows about the even/odd rule).

tatarize commented 3 years ago

There's other arguments in favor of it. It comes up kinda often in laser cutting too. People often clippath some stuff. Though I'm not even sure what kind of objects I'd need to generate. I'd guess a kind of group that contains the clippath as a clippath object. I've researched it before for meerk40t proper. meerk40t/meerk40t#70

abey79 commented 3 years ago

Sounds like it could get arbitrarily complicated. Would it make sense to make a list of the feature subset we'd intend to support? And have a small database of test SVG?

tatarize commented 3 years ago

Well, it shouldn't be that complex and since currently nothing is doing that work it shouldn't be too hard to invent an API and say this has always been the API, you've just never seen this object type. I try to kinda parse things so that it's helpful to anybody using elements but, not really doing anything too weird or fancy. I think just inserting a clippath object as a parent for anything with the clip-path property and checking it with the object in the shadow dom might work. Then you get a nice object without much weirdness that should pass the clip-path checks. Though I'd need to double check the interactions with CSS and how they style clip-paths attached to geometric objects. Since sometimes that changes the way things have to work.

tatarize commented 3 years ago

A clipping path is conceptually equivalent to a custom viewport for the referencing element. Thus, it affects the rendering of an element, but not the element's inherent geometry. The bounding box of a clipped element (meaning, an element which references a element via a clip-path property, or a child of the referencing element) must remain the same as if it were not clipped.

By default, pointer-events are not dispatched on clipped regions. For example, a circle with a radius of 10 which is clipped to a circle with a radius of 5 will not receive "click" events outside the smaller radius.

Seems right so far. Need to check some more stuff. The URL reference isn't really always needed for clippaths since they can exist as stand-alone objects outside of the defs. Which bodes fairly well, for the idea just rearranging them into a simpler but coherent parent objects.

tatarize commented 3 years ago

Multiple different path objects within the same clippath might be a bit hinkier to do there. And all forms of svg elements need to be added to that. So just a parent object might not quite be enough. It seems like it needs to reference an object that gets attached to the applied object. And for simplicity sake it would help if this kind of thing was handed down to all child objects since they are all equally clipped.

I'm considering maybe just having a value='clip-path' that very much just contains the clipPath element which also then contains whatever shapes are in that accordingly. It's a bit on the nose but it wouldn't be too hard to write that way. It's not much help to implementers other than parsing everything and giving well defined objects. if 'clip-path' in element.values: I have myself a group of objects in here to cut my current object with.

tatarize commented 3 years ago

It's implementable, safe, non-breaking, and provides all the information in line with how CSS/SVG is supposed to work. I'd just make the thing being referenced with the URL and plug it into that spot directly. It would get put into values and propagated to all the children.

It would maybe fail though with multiple nested clip-paths since I'd need the children objects to be the merged set of clip-paths but that's easier enough to update an already existent list, and use a copy() when doing that. Though apparently clip-rules can change between these updates. Which is a weird but obviously accurate problem. Since I wouldn't be able to tell what happened there.

Ah. I'd update but if the cliprule doesn't match the original cliprule, I get a nested clipPath instead of updated one.

I think that hits all the points. Weird edge cases and all.

tatarize commented 3 years ago

If a valid ‘clip-path’ reference is placed on one of the children of a ‘clipPath’ element, then the given child element is clipped by the referenced clipping path before OR'ing the silhouette of the child element with the silhouettes of the other child elements.

Hm. So clip-path on child objects of clipPaths are clipped themselves, before being clipped by the parent. That's a hell of a weird edge case, but also doable. But, since it can be placed on the clipPath element itself the element must exist and can't just sort of apply to the shape directly. I think I have the a sufficient set of requirements here.

tatarize commented 3 years ago

Clippaths can be applied to Groups. However this should just propagate down and affect all the elements within that group. So it might be a non-issue but is certainly something to put in coverage for.

tatarize commented 3 years ago

Clippaths have an inherent discongruity with how I process the shadow dom vs. the rest of the tree. I perform one process that iterates through the tree and yields a slightly modified tree with use and def objects replaced with their pure tree equivalents. However, objects like clipPath and mask and gradientfill occur within the shadow tree, and require to be compiled there into their Element objects in order to properly replace them in the IRI links. This required processing ordering may require some restructuring to either expose the IRI objects to the backend or moving the shadow dom into the main parse routine rather than merely simply omitting the shadow dom directly. The 1.4.x upgrade requires a bit more thought and research.

The styling doesn't matter to the clipPath object s only the raw geometry. So that gets done after the compiling of those objects. Whereas the objects themselves cannot be compiled until after they are styled since many of their attributes are presentation attributes. Several of these not-directly rendered objects need to be pre-rendered before the rendering tree. This may be before or after the use tree replacements occur. Which would push excluding defs to the final phase and where the IRI links get processed is still an outstanding problem.

Structure Tree could maybe be changed to flag elements as non-processed and replace the then either duplicate that functionality or integrate it into the main parsing routine. The IRI links mean that rendered objects must access the identified objects even those within the shadow dom. And they must be pre-compiled and linked at particular though currently unknown stages.

tatarize commented 3 years ago

There is an edge case bug where a <use> flag within a <use> flag within a <defs> will be returned to from the shadow dom. This is in error, because the def_depth is still 1 nothing should be returned. It solved the first <use> level and returned that to the outer parse which then inserted an errant shadow rectangle into the group.

tatarize commented 3 years ago

Rewrote the Dom, processes things correctly now there. The clipPaths applying to groups means that a clipPath group inside a clipPath group can have different rules for different clipPaths. And the objects within them. I think the best bet is to create the would-be IRI objects, do not propagate the clip-paths just have the links in the values. Give no object the clippath object so that there's room to correct this at a later date. Since the biggest problem is that all this data is stripped not how it should be restored.

Sort of a half measure, but quite able to be processed. Figure out the rest when things need to be actually implemented somewhere.

tatarize commented 3 years ago

Corrected in 1.4