Sunday, January 29, 2017

The Outline of a Solution

In this posting, I start the re-write of my mountain symbol code.  The first step is to generate an outline of a mountain.  But what I really want is a way to generate a smooth sequence of mountain shapes that vary from concave and pointy to convex and flat, like these (hand-created) examples:
So how can I do this?

If you examine the examples above, you'll see that the two sides of each mountain are mirror images.  From the first example to the third example, the line varies from concave at both ends to convex at both ends.  In the last two examples, the end point of the line (at the top of the hill) stays convex, but the start point of the line goes back to being concave.  So if we could have parameters that controlled the bend of the mountain at the bottom and at the top, we could interpolate between those shapes by changing the parameters from concave to convex and back again.

As it happens, this is essentially how Bézier curves work.  The math is complex, and I'm simplifying a lot, but with a Bézier curve you have a start point, an end point and two "control points" that determine how concave or convex the curve is near the start point and end point.  Another (simplified) way to think about it is that the control points are the direction the line moves off from the start (or end) point.  Here I've drawn in some arrows to indicate those directions:
You can see how the direction of the arrows has changed and caused the resulting Bézier curve to switch from concave to convex.

SVG has the capability to draw Bézier curves, so it's pretty easy (well, figuring out the control points is non-trivial, but otherwise it is easy) to create a routine that draws a mountain using two Bézier curves and interpolates in the way I've outlined above:
That first mountain might be a bit too concave, but I don't have to start at "zero" on this scale if it doesn't end up looking good.

One thing I know from previous experience is that it is nice to be able to generate mountains with rounded tops.  To do that, I can insert another Bézier curve into the mountain shape, connecting the two sides with a flat hump:
The ratio of width and height in these mountains, and how that ratio changes over the sequence is independent of shape.  So I can have narrow mountains or wide mountains that change ratio quickly or slowly:

One thing I notice looking at these shapes is that convex mountains -- the rounded humps near the middle of each sequence -- look visually "bigger" than the most concave mountains.  (And they probably are bigger in the sense of having more area inside.)  That part of the sequence also has a lot of very similar-looking mountains.  With some tweaking and adjustment I can reduce those problems.

So far I've been drawing the mountain shapes with SVG's Bézier curves.  But I actually want to break the curve up into a number of different pieces, so I can do things like perturb the outline.  Breaking an arbitrary path into segments turns out to be a fairly difficult problem, but I can use the browser's built-in capabilities to draw the curve, and then measure along it to chop it up into segments.  If I chop each mountain up into (say) 20 pieces, the result is mostly indistinguishable from the original curve:
Chopping into 8 pieces makes the segments more obvious:
Smooth isn't always better, in this case.  The number of segments will help determine the character of the drawn symbol, so I may end up wanting fewer segments.  We'll see.

It may be obvious, but since the mountain is symmetrical and has a peak in the middle, I have to be careful to use an even number of segments.  Choosing an odd number of segments chops off the top of the mountain:
So this provides the basic capability to define the outlines of the (new) mountain symbols.

No comments:

Post a Comment