einarwh / escher-workshop

Workshop: Functional geometry with Escher and Elm.
MIT License
87 stars 14 forks source link

Help on implementing the real Escher painting #1

Closed Arkham closed 5 years ago

Arkham commented 5 years ago

Hello there,

I've had so much fun doing this workshop and I learned a lot, so thank you very much for building this! ❤️

I also stumbled upon one of your blog posts here: https://einarwh.wordpress.com/2017/07/22/picture-combinators-and-recursive-fish and I was trying to implement it in Elm, but I quickly realized that I don't know anything about F# or about svgs :)

What I'm getting stuck is how to convert the more complex data structure for the Escher fish into an SVG. My current code looks like this https://github.com/Arkham/escher-workshop/blob/harder/src/Rendering.elm#L129-L147

toSvgElement : Style -> Shape -> Svg msg
toSvgElement style shape =
    case shape of
        Polygon { points } ->
            toPolygonElement style points

        Polyline { pts } ->
            toPolylineElement style pts

        Curve { point1, point2, point3, point4 } ->
            toCurveElement style point1 point2 point3 point4

        Path ( start, beziers ) ->
            Svg.path [] <|
                List.map
                    (\{ controlPoint1, controlPoint2, endPoint } ->
                        toCurveElement style start controlPoint1 controlPoint2 endPoint
                    )
                    beziers

Could you point me in the right direction? Thank you!!

einarwh commented 5 years ago

Oh, super cool! I think what you need for your SVG path is for your bezier curves to map to something like "C cp1x cp1y, cp2x cp2y, epx epy". So you can map each curve to something like that, and concatenate it into a large string with a blank in-between. In addition you need a starting point x0, y0 for your path, that you turn into the string "Mx0 y0", and finally you need to close off your path with "Z". So the d-attribute of your SVG path should be prefix-string + lots of bezier-curve-strings + "Z". You could try to see if this (rather messy) F# version makes sense: https://github.com/einarwh/safe-fish/blob/turn/src/Client/rendering/Reform.fs#L225

einarwh commented 5 years ago

So I would think roughly something like this (written without the help of a compiler):

toPathElement style start beziers =
    let
        toStr vector =
            Vector.toStringWith vector " "

        toBezierStr { controlPoint1, controlPoint2, endPoint } =
            let 
                cp1s = toStr controlPoint1
                cp2s = toStr controlPoint2 
                eps = toStr endPoint 
            in 
                " C " ++ cp1s ++ ", " ++ cp2s ++ ", " ++ eps 

        bezierStr = List.map toBezierStr beziers |> String.concat

        dval = "M" ++ (toStr start) ++ bezierStr ++ "Z"

        width =
            getStrokeWidthFromStyle style.stroke

        strokeColor =
            getStrokeColorFromStyle style.stroke

        fillColor =
            getFillColorFromStyle style.fill
    in
    Svg.path
        [ stroke strokeColor
        , strokeWidth <| String.fromFloat width
        , fill fillColor
        , d dval
        ]
        []
Arkham commented 5 years ago

Thank you so much! Was finally able to get it working 🎉

screen shot 2018-11-16 at 22 44 21
einarwh commented 5 years ago

Very nice, great work!

(A tiny detail, if full fidelity with the original Escher artwork is a concern, is that the eyes of the white fish should have a black pupil on white, with a black border around them... which is annoying, because it means you need something more than just a primary and secondary color!)

Arkham commented 5 years ago

Aha, indeed! I was starting to think that I should change the shape of the Path type to take an additional function Hue -> Style so that each path can decide how to be visualized :)

Anyway, thank you a bunch, I'll close this! 💯