timjs / elm-collage

Create interactive vector graphics and position them relative to each other
BSD 3-Clause "New" or "Revised" License
58 stars 19 forks source link

clipping support #31

Open blaineylab opened 6 years ago

blaineylab commented 6 years ago

Are there plans to support some forms of clip paths?

My use case is cropping a sprite sheet, for which I ended up patching Core.Image (Float, Float) String to Core.Image (Float, Float) (List (Float, Float)) String and rendering the points as a polygon clip-path attribute, e.g., clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);.

PS: this library is great in practice, and edifying to patch!

timjs commented 6 years ago

Clipping would be a very nice addition to Elm Collage! Currently I've no plans to implement it myself however. It should be not that hard to add. Svg already has support for it as you already observed I think. A good interface to introduce clipping would be good. Obviously pull requests are always welcome!

Did you check if the functions in Collage.Layout can help you with this btw? Also, can you embed (a link to) an image showing what you like to do? Really helps brainstorming I think!

PS: Good to hear you like the library!

blaineylab commented 6 years ago

I didn't see a way to make a clipping mask using Collage.Layout but I may have missed something. I guess the ability to include html might allow clipping something in the Svg package, then bringing it in? But it would be nice to have a uniform interface.

Here are examples of the source image and program.

What do you think would be the best svg to generate? Not very familiar with the spec but it looks like creating a <clipPath> and including it by reference might be a more standard alternative to the clip-path attribute.

timjs commented 6 years ago

Thanks for the pictures, helps a lot! Collage.Layout cannot do this yet indeed.

So what are the possibilities to implement it? If I summarise your ideas correctly we have:

  1. Add a clip-path attribute to the generated Collage
  2. Create a separate <clipPath> tag and reference it in the generated Collage

Is that correct?

Some inspiration for a functional interface:

Kwarrtz commented 6 years ago

From what I can glean from this documentation, you use the clip-path attribute to reference a <clipPath> tag containing all of the actual information about what to clip, similar to how gradients are defined in a separate tag from where they are actually used. So it looks like there's really only one option for how to implement this.

Also, would it be worthwhile to implement <mask> while we're at it?

Kwarrtz commented 6 years ago

One question is how exactly we want clip paths to be specified. In SVG, a clipping path is just a collection of arbitrary SVG elements (i.e. Collages). This very straightforward and flexible, but it also means passing in a lot of unnecessary information since color doesn't matter and 99% of the time you're just passing in filled shapes anyway. It would be much cleaner to be able to just specify a clip path using a list of Shapes, but that doesn't give us anyway to specify rotations or relative positions, which is important for clipping. We could define an entirely new API, built around something like

type alias ClipPath = Transform { shape : Shape }

but that would require us to basically reimplement all of the transforming functions (shift, scale, etc) on ClipPath. This could be made a little less painful if those functions were made more generic, but that would probably require exposing Transform (Elm still doesn't have typeclasses right?) and would be a breaking change. Also, masks usually make use of gradient and pattern fills, so if we want to support masks as well we'd need to somehow incorporate those into the API.

Given the can of worms that a bespoke API would open, I'm inclined to suggest we just implement clip paths using Collage, at least until the next major version.

timjs commented 6 years ago

I'll have give this an extra thought. Haven't use the library for some time (about half a year 🙈), going to do that soon. Probably I'll have some more ideas about what the best way to go will be.

The ClipPath type alias looks compelling. But because of the abstraction sealing of Elm this will be hard indeed...