waterbearlang / waterbear

Visual block syntax for programming languages
http://waterbearlang.com/
358 stars 88 forks source link

1272 offscreen buffer #1276

Closed dethe closed 8 years ago

dethe commented 8 years ago

Added these blocks:

And an example program which draws on a blank image and then draws the image.

ghost commented 8 years ago

The two new shapes don't work with a closed, connected path. Here's a gist showing that : b0ec4a40e14ab4a4c817 . The issue is the moveTo() function is called regardless of if it is a closed path or not and that breaks the context of the Path.

ghost commented 8 years ago

Also I find that it isn't very obvious what the draw on image block is for. I only figured it out once I went to the issue and read the description of what you wanted to achieve. I don't know if the name just isn't clear but I think it would be helpful for the example to draw the image more than once to reinforce the function of the block.

dethe commented 8 years ago

Cool, thanks for catching that, will try to fix tonight.

dethe commented 8 years ago

Any ideas how to make the draw on image easier to understand? I'm trying to make it more clear than the more computerese "offscreen buffer"

kotarCreative commented 8 years ago

Could you say "create new tile?" or "draw new picture?"

dethe commented 8 years ago

Draw on image and create blank image are two things, although I just realized I forgot an important step if they draw on an existing image. I need to draw the original image to the canvas before they draw.

CelticMinstrel commented 8 years ago

So, for your regular polygons and stars, you're defining it by the enclosing circle? I don't quite understand how you're defining the regular star though. Aren't the inner and outer radius completely dependent on each other for a regular star polygon? Maybe we're thinking of different things...

dethe commented 8 years ago

Just alternating my way around the inner and outer circles. Happy to get pointers to better/different star drawing strategies. Mine are just what first came to mind, not based on any particular math or theory.

CelticMinstrel commented 8 years ago

I see. I guess that would work for a generic way of drawing a star, but it won't be a regular polygon. (Not that it needs to be regular!)

I know how to draw a regular star polygon with turtle graphics, but I'm not quite sure how that would translate to JS canvas. It would look something like this (using UCB Logo):

to star :size :points [:denom 2]
  ; I call the optional parameter "denom" because it's the denominator of
  ; a ratio called the "Schlafi symbol"
  repeat :points [forward :size right (360 * :denom) / :points]
end

The code above doesn't account for reducible ratios, such as 6/2 (which could represent a Star of David but instead just produces a triangle). If "denom" is set to 1, it reduces to a regular polygon, so that method is a generalization of regular polygons. I only specified it to default to 2 because I named the procedure "star", so that means it'll always give a star for any odd number of points.

If I understand what you've said correctly, the stars produced by that would be quite different from the ones produced using your method – your stars don't actually have crossing lines, right? I'm guessing they're actually concave polygons, with all sides the same length, but two different alternating internal angles (and thus, non-regular) – what the Wikipedia article refers to as "isotoxal".

dethe commented 8 years ago

@CelticMinstrel yes, isotoxal star would be the formal description, thank you. They have the nice property that they can cope with any number of points (greater than 3) and not just odd numbers. By increasing the difference between inner and outer "radius" they can remain pointy too.

dethe commented 8 years ago

@AdriennePind I spent some time looking at the code and thinking about how this "should" work (for some value of "should"). Here are some thoughts I'd be interest in more feedback on.

My understanding is that the three options currently for drawing multipath shapes: Connected, Connected and Closed, Disconnected, are only really needed because our implementation of the underlying shapes makes too many assumptions about how they will be used. Namely, they call beginPath() (which implicitly ends the earlier path). Simply calling moveTo() doesn't end a path (you can test it here: http://jsbin.com/vegoqu/edit?js,output ), but we are pretty loose about where we call beginPath(). We also have things scattered across runtime.path and runtime.shape (and the util namespace too) and should unify it a bit. What I think the answer is would be to use the context of the blocks and make the path blocks somewhat context sensitive.

  1. Any shape block would not call beginPath() if there was already a path in progress. When we call beginPath, we track the existence of a path in the runtime, and which block started it. When that block (and no other) returns it ends the path (by changing state in the runtime, no need to call closePath()).
  2. If the first vector argument is the special vector Last Point, don't issue a moveTo(). Some shapes (rect) don't need a moveTo either. This will take a little bit of bookkeeping to keep track of. Last Point can always resolve, starting at <0,0> but after we've done any drawing always carrying the actual last point value. Arcs have a funny relationship with lastPoint. To trace an arc as part of the radius of a circle you want the last point to be on the edge of the arc, but to fill a pie wedge you want the lastPoint to be at the origin/centre of the circle. Not entirely sure how to resolve this.
  3. A shape does not need to be marked as closed to fill() it. It doesn't even have to be closed. So we really never need to call closePath().
  4. If the shape is used as the clip shape in a clip context, don't draw it (although I think the canvas API already takes care of this).

Cleaning up the path/shape namespace API can also reduce what is currently a lot of redundant code. Both of the constructors may have made sense at one time, but haven't for quite a while.

If this works, we could drop the path "type" altogether and still be able to draw paths either on their own or as part of a larger shape.

Thoughts?

CelticMinstrel commented 8 years ago

They have the nice property that they can cope with any number of points (greater than 3)

Not ≥ 3?

Arcs have a funny relationship with lastPoint. To trace an arc as part of the radius of a circle you want the last point to be on the edge of the arc, but to fill a pie wedge you want the lastPoint to be at the origin/centre of the circle. Not entirely sure how to resolve this.

If you want to fill a pie wedge, I think your path should be the sector rather than just the arc – in other words, you should be drawing a radius as well as the arc (either before or after the arc, or even both).

ghost commented 8 years ago

@dethe Those points make sense to me, although I'm still not convinced that the moveTo() function doesn't break the path. I agree that it shouldn't but if you look at your code for the star and regular polygon you aren't calling beginPath() but they still break the context of the path. It's possible that there is another reason why that is happening that I haven't noticed though. As for the point 1, that is what I have implemented, beingPath() is only called if drawingPath == false. For point 2 I also thought that would be a good idea, just having lastPoint as a boolean to indicate if moveTo() should be performed or not. I moved away from that idea because it became pretty complicated when I went to implement it, for example when lastPoint is used before anything has been drawn you still have to do a moveTo() or else nothing will be drawn. Also I though that lastPoint should actually return a point since it could be used elsewhere. However you may have a better implementation for this. I'm also not really sure how to deal with the arcs, as it stands now there will be a line implicitly drawn to the beginning of the arc (since no moveTo is called). For points 3 and 4 I think that would be fine. I also agree that we could get rid of the Path type all together and just pass a function for drawing each shape, that was the next thing I was thinking about doing before we decided to merge.

dethe commented 8 years ago

Uh, yes ≥ 3, sorry. Been organizing an event for 9 different partner groups for the past several months and it was yesterday. Haven't been at my best in the days leading up to it.

I think I agree about the arc, except when I look at the results of both drawing and stroking an arc by itself and just can't imagine how anyone would actually want either of those results. Just bizarre, but by no means the worst part of the Canvas API.

dethe commented 8 years ago

@AdriennePind I hope it's clear I'm not asking you to change your implementation or to implement the though experiments above, and in case it wasn't clear before it should be now.

ghost commented 8 years ago

@dethe yes no worries I did not think that :)

dethe commented 8 years ago

Ready for re-review:

dethe commented 8 years ago

@AdriennePind Ah, thanks for catching that. I thought I'd gotten the lineTos and moveTos in the right places, but missed that one. If anyone can think of a better name for isDrawingPath that might help.

Should be good now, your gist is closing now when I try it.