google / clojure-turtle

A Clojure library that implements the Logo programming language in a Clojure context
Apache License 2.0
425 stars 41 forks source link

Commands for filling shapes #2

Closed jeisses closed 8 years ago

jeisses commented 8 years ago

Hi,

I needed the turtle to be able to fill polygons instead of only adding lines. The way to do this that I came up with is by adding 2 new turtle functions:

Once start-fill is called, a filled white polygon will be drawn instead of lines. This is done by utilizing the same lines atom and inserting 2 symbols to identify polygon drawing: clojure-turlte-filled-polygon

There currently is an issue that you can't see the polygon until the end-fill function is called, then it pops up. Other than that this seems to work.

@echeran Is this something you'd be interested to see in Clojure Turtle? If so, what do you think of the approach I'm taking? I could make some adjustments and fix the issue above if needed.

Thanks

echeran commented 8 years ago

This idea is cool! I think this is a good feature to add. Even though we're going outside the traditional basics of Logo, I still think there clearly is room to expose useful drawing functionality from Quil (Processing) through the simplicity of Logo (turtle graphics) without sacrificing too much of the simplicity of Logo. I've already been thinking about adding color (color = color of the lines, aka "line fill"), and adding functionality for filled shapes is the logical counterpart to that.

In order to add start-fill / end-fill to lines, the lines should be generalized from a vector-of-vectors into a map, with the keys :from and :to. Then, :start-fill and :end-fill could be optional keys in those maps whose value is true (or at least "truthy") when they are present in the map.

I think it might be possible to draw lines before the shape is filled. (I haven't tested this yet.) It might require creating extra state in the loop form called fill-lines that is a seq that copies all line maps between a start-fill and an end-fill. Every iteration of the loop form will draw a line, regardless. If a line map contains :start-fill, then fill-lines gets reset and that line map (and all others following it) will be added to fill-lines... until a line-map with :end-fill is seen. We draw that final line, then fill-lines gets emptied and turned into a set of commands to achieve the filled shape (preferably a fill shape with stroke/line width of 0, if that makes sense). The trick may be in how to combine :start-fill and :end-fill into line maps by the start-fill and end-fill fns. start-fill can add the :start-fill key temporarily to the turtle map, and the forward fn can dissoc it from the turtle map and assoc it into the next line map that it creates. end-fill can assoc :end-fill into the last line map in the lines atom.

What I also don't know is: what happens when you call begin-shape more than once before end-shape?

And another thing to think about is what should it mean to a filled shape if the turtle picks up / puts down its pen in the middle of creating the shape? My guess is that this should not affect filled shape behavior too badly.

jeisses commented 8 years ago

Yes I like that idea, it'd be nice to have a way to handle more commands (like stroke-color).

Extending the lines vector to be more generic seems like the right approach. I'm not sure yet if I get the method you propose. The line maps that have the :fill key set will be rendered as vertex instead of lines?

An other option I was thinking about is to replace the lines atom with something like a commands atom, that contains a vector of vectors, where each vector represents a turtle action. This vector could look something like: [[:stroke-color [255 0 0]] [:line [10 10 100 100]] [:fill-color [0 255 0]] [:fill [100 100 120 120 120 140]]] E.g. the first element in the vector is the type of action, the second element is the arguments for that action (for color it are RGB values, for line is x1 y1 x2 y2 and for a filled shape it's a list vertices x,y coordinates).

It's true that the fill commands currently behave not so nice; only forward, left and right commands are supported after calling start-fill. And calling start-fill 2 times behaves weird, I'm not sure how this is supposed to work in Quil.

echeran commented 8 years ago

I pushed a change to master for setting the color of lines. I used a map instead of a vector of vectors to represent a line. That much was fine.

I implemented my proposed idea for a fill shape in the branch u/echeran/shapefill. I'm not terribly impressed by what I came up with. (I hacked up lines to have a line of length 0 to handle a start-fill or end-fill call.)

I'm going to think about your idea of replacing lines with commands. I think that might be the proper way to generalize... By doing so, it would be as if we're logging the actions or treating them as facts, so that sounds Clojure-y and data-oriented, which is good. We'll need a function that loops through commands and converts that into a set of Quil function calls.

Try out the filled shape code and see if it works the way you think it should. If so, I can merge that into master, just for completeness' sake. And then afterwards, we can work on replacing the logic of lines with commands.

jeisses commented 8 years ago

Nice, the u/echeran/shapefill branch does exactly what I wanted. Also it's more concise, and stroke- and fill color are both settable. The 0 line-length for the fill calls isn't too bad.

So this PR can be closed if shapefill gets accepted :)

echeran commented 8 years ago

Thanks for closing out, @jeisses . I couldn't bring myself to do it until I had converted the drawing impl to be commands-based rather than lines-based. I finally finished doing so and pushed those changes. I think the code is a little cleaner now than it was before. Thanks!