Thursday, December 8, 2016

How To Decorate a Mountain

My previous post ended with Dragons Abound producing mountains like this one:
There are still a few problems I want to clean up and a few details I'd like to add to improve the quality of these mountains.

The first problem is that the program still occasionally scribbles outside the lines:
That's undoubtedly due to a mistake in the logic of stopping and starting scribbles across the concavities, but I don't feel like trying to untangle that problem any further.  Instead, I'm going to apply an SVG clipping path to the scribble area.  This will clip off any scribbles that go outside the scribble area.

You might wonder why I didn't just use a clipping path in the first place and scribble back and forth across a big bounding box and let the clipping path fill in the scribble area.  The problem with that approach is that you lose the nice "turnarounds" at the ends of the strokes, and that's really what makes a scribble look like a scribble.  You end up with something that just looks like hatching:
This image illustrates the next problem:
The red line here is the "ridge line" that comes down from the top of the mountain and forms the edge of the shaded area.  Because it is seen end on in perspective, the back-and-forth line of the ridge is exaggerated.  But you can see the problem:  The perturbation up at the top is enough to send the ridge line outside the silhouette of the mountain.

I can solve this problem by scaling the perturbation by the width of mountain.  Up at the top where the mountain is narrow there will be less perturbation, and down at the bottom where the mountain is wide there will be more.  Measuring the width of the mountain at every point down the line is tedious, but it turns out that it is sufficient to scale the perturbation as if the mountain were a perfect equilateral triangle.

It's hard to show an example of a problem that isn't there, but here's a few mountains generated with these fixes in place:
Now let me move on to some additional features.  When I compare the above mountains with these examples:
I notice that the hand-drawn examples have additional details on the lit side of the mountain.  Basically just lightweight, random scribbles running up the face of the mountain.  I'd like to add something like that to my mountains.  A starting point might be a line like this:
I call this a "minor ridge line" because it represents a ridge running down the mountain, not end-on to the viewer like the ridge line that separates the lit and shaded sides, and not in total silhouette like the two sides of the mountains, but rather at an angle in between.  Adding some shading can improve this illusion:
(Ironically, my "hand-drawn" examples look more mechanical than the computer-generated examples!)

To start with, let's see if I can get a line down the lit face of the mountain.  To start with, I'll make a line from the peak of the mountain down to the middle of the lit face, and pick a spot about halfway down:
That will be the starting point for my minor ridge line.  Then I'll pick a spot on the baseline of the mountain to create a line at about the same slope as the right side of the mountain:

Let's see if I can generate that.
Okay.  It looks a little odd if this ridge line is straight while the mountain side is concave, so I'll add some code to make the ridge line approximately as concave as the mountain side.
Now I need to add some perturbation.  Since this ridge line is roughly at 45 degrees to the viewer, I'd expect to do some perturbation in the Y axis (as with the mountain sides) and some in the X axis (as with the main ridge line).  However, it turns out that adding perturbations in the Y axis looks weird:
So I only add perturbation in the X axis:
Now I'll set the color to black, use the "hand-drawn" line, and make the line grow from narrow to wide:
This might need some additional tweaking -- that center line is pretty angular -- but there are lots of knobs to play with.

In the meantime, I want to try adding some shading above the minor ridge line.  The line itself is a high spot on the mountain, so this shading represents the low spot or hollow behind the ridge.  Like the minor ridge line itself, I want the shaded area to be narrow at the top of the ridge and wide at the bottom.  I can reuse the ridge line as one side of the shaded area, and then add another line that starts from the same point but gets wider near the bottom.  If I apply the same perturbation to both lines, I get something like this:
Then I just need to scribble in that area with some shading.  Because this area is relatively small and I don't want it too dark, I use lightweight lines.
This minor ridge line won't appear on all mountains; it will be an option to add flavor.

Another detail I want to add doesn't appear in my examples of hand-drawn mountains, but is something I think can look good.  The idea is to add some additional lines along the right side of the mountain to suggest structure.  Where there's a sharp angle in the right side line, I'll extend the mountain line along one of the existing lines, as with these Photoshopped examples:
To start off implementing this, I write a routine to scan down the right side of the mountain looking for angles between 0 and about 90 degrees.  I start the scan part-way down the mountain, because putting a horizontal structure line up near the peak is likely to run into the shading area.  Here's an example of a mountain with the potential structure points identified:
Now I have to (randomly) select one of the two line segments and extend it.  I'll also taper the line so that it trails off.  Here are some examples of what this looks like in action:

I'm pleased with how this turned out -- it's a subtle effect, but it really adds to the "hand-drawn" quality.  I can do the same thing on the ridge line that defines the shaded area.
Implementing these last two details forced me to write some code to try to keep the decorations on the mountain from overlapping.  One thing the human eye does effortlessly is find open space, and not draw one thing on top of the other.  That's much harder for the computer.

This is getting long, so I'll save some more of the detail work for next time.  On a final notes, sometimes the mistakes can be striking:

Saturday, December 3, 2016

The Shape of Things To Come

In a couple of previous posts, I described how Dragons Abound makes "hand-drawn" lines and fills areas with scribbles.  These are some of the basic functions I need to create "hand-drawn" illustrations of mountains for maps, similar to these sorts of examples:
In this posting, I'm going to look at how to draw the shapes of the individual mountains and their shaded areas.  (In a later posting I'll look at how individual mountains are combined into small groups.)

The basic mountain shape is a triangle.  In my hand-drawn examples, there's quite a bit of variance between the two sides in slope and starting point:
To capture this, I'll add some code that will make the basic triangle shape vary in both ratio and slope of the sides:
Another feature of the hand-drawn examples is that the mountain sides are bumpy rather than straight lines:
In some cases the silhouette appears to be several mountains merged together -- and I'll get to that eventually -- but even the single mountains have irregular sides.  Following the example from /u/negative34 over on Reddit, I'll try using some gradient noise to displace the mountain sides.

My first attempt is to re-use the code that perturbs lines to make them look hand-drawn.  I'll also use a line-drawing routine that produces sharp corners rather than smooth curves, and make the mountains a little broader.

Sometimes this looks okay -- the first mountain here is not bad.  But often the mountains have odd spikes/bumps "sticking out", as in the second two examples.  I think the problem is that I'm adding perturbations at right-angles to the lines.  In the hand-drawn examples, the perturbations are almost always in just the height (Y axis) -- the bumps point up, if you will.   Here's what that looks like:
That looks much better to my eye.  I'll try twice as much perturbation:
That also looks pretty good to me -- some interesting silhouettes there.  (But notice something broke my scribbles on the first mountain.)  Doubling again:
That's perhaps a little too jagged for my tastes, and this is starting to look like a whole collection of smaller mountains than a single mountain.  There's also some breakdown in the actual polygon, where the perturbation has caused a piece of the polygon to "reverse".  A value somewhere between the first and second values seems about right to me.

Another adjustment to look at is how "curvy" to make the mountain outline.  I don't like the way the mountains look with significant curves:
But a small amount of softening of the vertices seems good:
One area where these mountains are still sadly deficient is the the shadow line down the center of the mountain.  In the hand-drawn examples, this line is usually wavy to suggest a ridge line:
To make the shadow line wavy, I'll perturb it using noise the way I did to the mountain outline itself.  But in this case, I'm perturbing in the X axis, to make the line curve back and forth, rather than up and down.  After playing around a bit to get a good level of noise, I have this:

This immediately makes the mountains look considerably better -- although perhaps I've just been staring at the straight shadow line so long it seems like a bigger change to me.  Human artists often draw a line down the ridge, so let me try that:
That looks okay, too, although it gives a kind of two-dimensional quality to the images.

Another thing I've noticed looking through the human-drawn examples is that the sides of the mountains are more often concave than convex:
Arguably this is because real mountains tend to have this sort of curve due to mass wasting, where the slopes of the mountain collapse downhill due to gravity.  The perturbation of the sides of my mountains sometimes produces this sort of profile, but I can help that along by making the starting point be a gentle concave curve:
The amount of curve here might look drastic, but seems to work pretty well once the mountain sides have been perturbed:
Now I want to look at the proportions of the mountain.  Looking at the human-drawn examples, it is striking how often the mountain (or mountain cluster) fits the golden ratio:
The golden ratio is said to "...yield pleasing, harmonious proportions" and is often seen in nature, so it's not surprising that it should be an intuitive choice for illustrating a mountain.  I'll take advantage of this to size my mountains (with some variation) around the golden ratio.

Using everything I've developed so far, let's compare the current mountains with the original:
Okay, pretty good progress!  :-)  Next time I'll continue refining these mountains, but here's the quick and dirty version of a mountain range:

Wednesday, November 30, 2016

More Scribbles

In the last posting, I described the process Dragons Abound uses to create scribbles, as shown in this example:
In this posting I'll talk about making these scribbles more natural looking.  I'll use my usual simple mountain as an illustration:
The first refinement I want to try is to make the lines of the scribble into gentle arcs.  When people scribble lines back and forth, they typically pivot their drawing hand around their wrist.  As a result,  they draw shallow arcs rather than straight lines. 

To make each segment of the scribble into an arc, I will add a new point at the midpoint of the segment, and offset that midpoint a short distance along the perpendicular to the line:
The new (blue) line from `P_0` to `P_1` passing through `M'` will then form a shallow arc (because I'm using D3's ability to interpolate between points on a polyline with smooth curves).  One point to note here is that every other stroke of the scribble I have to swap the offset from side to side.  This is necessary because the first stroke goes up from bottom to top (in the Y direction) and the following stroke comes down from top to bottom.  It's also a good idea to scale the offset based upon the length of the stroke, or short strokes will have a stronger curve than long strokes.

Aside:  As I was working on this I discovered a bug in the code I borrowed from the XKCD plot in Javascript.  I fixed that problem, sent a patch off to the author, and then found a problem in my code that had apparently been masked by the problem in the other code (!).

Here's a comparison of the original straight stroke to a (strongly) curved stroke:
And here's a version with slightly less curve and the hand-drawing jitter turned on:
This doesn't look good.  There's a kink in the middle of each arc, particularly noticeable in the longer lines.  What's going on here?

Remember when I said the arc was created using D3js's curve interpolation?  It made a smooth arc between the three points defining each stroke, because there was a lot of "room" between the points to fill in a curve.  But when I turn on hand-drawing jitter, those three points are interpolated into many points, much closer together.  And since those points are fixed, D3js's curve interpolation can't move them to make a smoother curve.

The solution is to do the curve interpolation first, and then the jitter interpolation.  Unfortunately, D3js doesn't provide any (easy) way to do this.  After an email exchange with Mike Bostock, it looks like the best approach is to draw the original curve, and then interpolate it by measuring along the curve and sampling points. These points can then be jittered to make a new line.  Here's the arc+jitter version along with the straight and the arc versions:


It's a minor difference at this step but worth keeping.

The next refinement I want to implement is to break up the regularity of the distance between the scribbles.  I'll do this by varying the step size as I sweep the finder line through the polygon.  I randomly offset the step size by up to half the step size smaller or larger.  Here's an example alongside the previous versions:
Now the scribbles vary in spacing, but the upward strokes all have precisely the same angle.  I can address that by varying the angle `mu` of the finder line as it steps through the polygon.  I'll also turn on the code the varies the width of the line.  Some examples:
And some denser examples, probably more similar to what I might use on a map:
(The densest of these takes about 3 seconds to render on my desktop machine, by the way.)

One problem with the examples above is that the shading precisely fills a very regular shading area -- this is particularly glaring in the denser examples.   The shading area is defined as the left side of the triangle:
I can make the outline of the shading area less precise using the same perturbation that I use for making hand-drawn lines -- here's what that looks like with increasing amounts of perturbation:

You can see by the last example the perturbation is pretty extreme -- that looks like a shading area as drawn by a drunken Picasso.  Somewhere around the third example looks right to me -- here's what mountains look like when shaded with that level of perturbation:
That looks better than the very precise shading area, but notice that the shading area now sometimes goes outside of the mountain.  That's not entirely unpleasing, but that rarely (if ever) happens in the hand-drawn examples I've looked at.  So I'd like to modify the perturbations so that they can only go inward.

The perturbations in the hand-drawn line code come from this:

// Generate some perturbations. 
var perturbations = smoothLine(points.map(d3.randomNormal()), 3);
The function d3.randomNormal generates numbers with a mean of 0 and a standard deviation of 1.  It turns out that if you are perturbing a polyline that is in counter-clockwise orientation, the negative numbers perturb the line inward and the positive numbers perturb the line outward.  So to perturb only inward, I can just use the negative numbers.  (And ensure my polyline is in counter-clockwise orientation!).  This is the result:
 You can see that the shading now generally avoids going "outside the lines".  As the last two examples show, it can still happen in sharp corners -- because the inward perturbation actually pushes the line back so far it crosses back over the other line, creating a pocket outside the shading area, e.g.,
I don't see offhand any straightforward way to deal with this, so I'll just live with it for the moment.  It may be that when I get around to creating the shading area for real, I'll use some better approach to perturb it and this feature won't be needed for shading anyway.

So I'm now able to make a "hand-drawn" line and scribble within an area.  In the next posting, I'll start looking at creating a more convincing mountain shape than the simple triangle I've used so far.