Tuesday, March 14, 2017

On (Erkaman's) Cloud 9

In a previous posting, I discussed a couple of ways to illustrate a forest on a map:  By drawing individual trees and by drawing a forest mass.  Both trees and forest masses are often drawn with the a bumpy edge like a cotton ball:

So how do you make a shape with fluffy edges?

Well, about a month ago /u/erkaman made a posting to /r/proceduralgeneration about creating procedural clouds.  And you know what clouds have?  Fluffy edges.
In his Github repo, Erkaman explained his technique:

So it's just a matter of stealing adapting this approach to produce trees or forests.  (In fact, you'll see I made this comment on his original posting.)

I'll start off by implementing a function to take a line segment and turn it into a curve.  I already have a function that will take a line segment and offset the middle on a curve:
But that's not quite the right look.  It's better to use a real Bézier curve to get the right rounded shape.  Fortunately, I had tackle Bézier curves for mountain outlines, so I have some code as a starting point.  It isn't exactly right for this use, but it isn't too hard to tweak it:
The shape of the curve is determined by a control point for each end point.  Here are the control points for this curve.
Aw, who's a sad Bézier curve?  Ahem, well, moving on...

The shape of the curve can be varied by moving the control points around.  If I vary the control points separately, the curve can be asymmetric.
This gives me a great idea for procedural generation of Muppets!  No, wait, don't get distracted.

This turns a line segment into a curve, but for my purposes I need to turn it back into line segments.  (One reason is that I'm going to want to connect a series of these into a single polygon, and I can't easily do that with Bézier curves.)  I already have a function to chop an arbitrary SVG path into line segments (again, from the mountain work) so I can apply that.
The curve can be made smoother or choppier by changing the number of line segments.  These are eventually going to get shrunken to map size, so they don't need to be very smooth.  Once the curve is in line segments, I can apply all the usual hand-drawn line effects, such as smoothly varying the width of the line, perturbing it slightly, etc.
The next step is to apply this to a polyline (a sequence of line segments), like the ellipse in /u/erkaman's example.  In my case, the polyline will be the outline of the forest mass, or the outline of the top of a single tree. 

Applying this to a polyline is done by stepping through the polyline so that the end of one segment is the start of the next segment, so that all the curves will connect.  The only tricky part here is that we have to throw out the last point in each curve so that it doesn't duplicate the first point in the next curve.  Bad things happen when you have duplicated points and try to draw an SVG curve through those points!
And there you go, my own fluffy, hand-drawn cloud.  You can see where the curve starts and ends because the line widths don't match up; this is not noticeable at map scale.  Next time I'll start making use of this.

1 comment: