Wednesday, August 29, 2018

The Grasslands (Part 1)

Right now, Dragons Abound marks two kinds of terrain: mountains and forests.  It has a variety of ways to illustrate each of those.  I want to work now on adding markings for grasslands (plains).

Many maps don't mark these terrains at all, or indicate them with color.  Among the maps that do mark plains, a sort of arced symbol of hash marks is the most common choice.  Here's an example from a black and white map used in James Barclay's Raven novels, and drawn by Jenifer Mundstock
The little hash marks look something like grass, and I suppose that's why it is used.  Here's another example on a map from Misty Bee:
While the hash marks are by far the most popular option, there are some variants.  For example, on her Tamriel map, Misty Bee marks plains with sparse dotted lines:
On a few maps I've seen plains or grasslands indicated by just a few occasional trees, as in this map from John Stevenson:
Since I already have a routine to draw individual trees, that's a variant I may explore at some point.

However, by far the most common indicator are the hash marks, so in the next posting, I'll start working on drawing those.

Thursday, August 23, 2018

Various Miscellany (Part 3)

Some current statistics (8/23/18):

    Source lines of code:  23,776
    Dead/removed lines of code: 7,202
    Sources lines of code in libraries: 20,774
   
I realized my previous code counts were misleading, as I was including packages I didn't write myself (such as d3.js).  This motivated me to move all the various libraries I use into a separate directory, although the SLOC count of the libraries is not entirely accurate, since there are multiple versions of some of the libraries (e.g., both the full and the minimized Javascript).
Much of my development work on Dragons Abound focuses on some theme or another:  forests, city icons, etc.  But I intersperse that development with shorter efforts where I do things like refactor code, fix bugs or implement small features.  Every few weeks I try to spend some time generating “random" maps and seeing what problems are revealed.  Developing new features often breaks old features or they interact in unexpected ways.

To aid with this, I recently made an effort to go through the default parameters and set them to values that can generate a wide range of random maps.  (As opposed to particular map styles, where I lock the parameters down to specific values to get a specific look.)  As I was doing this recently, I realized that I really had three different kinds of parameters.

First, I have parameters which are essentially constants.  Typically I created these because I didn't know what value would work, but then I discovered that there's one “best" value.  An example is a parameter called “mtnToplineSegments" which controls the number of segments in the topline of a mountain icon.  This parameter needs to be high enough to provide a reasonable amount of detail in the icon.  For example, a mountain icon with just 2 topline segments would be a triangle -- kind of boring.  On the other hand, I don't want this number to be too high, because it increases the complexity and time it takes to generate and render the mountain icons.  In the end, it turned out that 7 was a pretty good value for this parameter, and there's really no need to change it.  So it's basically a constant.

Second, I have parameters which provide some variation in the individual elements as they are generated.  For example, “mtnRatioRange" defines the range allowed for the ratio (of height to width) of the individual mountain icons.  I don't want all the icons to be the same ratio, so this provides some variation.  By default, this is “[1.25, 1.50]" which means that all the mountains are 25% to 50% wider than they are tall.  During the generation of each icon, a random number is picked in this range and that becomes the ratio for that icon.  I call these “variation" parameters.

Third, I have parameters which provide some variation from map to map but which need to be the same for all the elements on one map.  An example of this kind of parameter is “mtnShadeType".  This parameter controls how shaded areas on a mountain icon are going to be drawn, and it can have a variety of values:  scribble, gradient, contour, flat, gradient+contour, or flat+contour.  But whatever value is used, it needs to be the same value across all the icons on the map.  It doesn't make sense to have some mountains illustrated one way and some another.  So for these kinds of parameters, I really need to make a decision about the value before I start generation.  I call these “choice" parameters.

Confusingly, the parameter types can overlap.  For example, suppose I want to have some maps where the mountains are tall and narrow and others where they are short and wide.  I can control the ratio of the mountains through the “mtnRatioRange" parameter, to get either kind of mountain.  But to get consistently tall and narrow across a particular map I'll have to treat mtnRatioRange first as a choice parameter to get a range, and then as a variation parameter.  Combine this with parameter styles as I discussed recently, and it can quickly become quite confusing to track parameters!

One of the first random maps I ran used custom city icons, i.e., the kind drawn by a person and read into Dragons Abound from a file.  But all of the icons were clustered in the middle of the map:
I've seen similar problems before.  The way Dragons Abound places something like an icon is to draw it (or import it, in this case) in the middle of the map and then move it to where it belongs.  When stuff ends up clumped in the middle of the map it's because that update never happened.  This was a little puzzling because I know this style of icons had been working previously.

I figured out that the icons worked if they were allowed to move around the map (like the larger icons are allowed to do to keep them from clashing with something else on the map) but were not working otherwise.  Apparently, these style icons were not being moved to the city's location at the beginning of the simulated annealing labeling routine.  If the simulated annealing labeling routine was allowed to move the icons it would move them to the correct spot.  Otherwise they just sat where they were placed.  The solution was to (duh) start them in the correct spot:
And that takes care of that.

Here's a problem with the mountains on that same map:
You can see where I've circled in red that some clefts have been added to the mountains.  But instead of being mostly vertical, these clefts are slanted too much to one side.  I'm fairly certainly the problem is in a routine that adds “lateral breaks" to the mountain's topline, but this sort of thing can be hellish to debug because it's really hard to look at the numbers and figure out if they're right or wrong.  So I revive some of my testing code that draws a single mountain with lots of debugging outputs.

Fairly quickly I find at least one problem.  I have a routine “findPointAtX" that walks along a polyline (like the topline of a mountain) and finds the first line segment that contains a particular X coordinate.  It looks like I rewrote that routine at some point and made a subtle change in the return values, and that has broken the routine that adds vertical breaks to mountains.  Taking account of the new return values fixes vertical breaks:
The vertical breaks are where the descending lines are drawn.  So that's fixed, but I use findPointAtX in a number of places; I need to check all of them.

The next problem that pops up is this:
The ridge line from the dip to the corner of the mountain is supposed to go mostly down and some to the right, but it's being drawn to the furthest right point of the baseline.  It's fairly easy to find the point where the ridge line is created and spot the problem.  When I went through the default parameters to set up a good variety for random mountains, I fixed the name of this parameter -- but missed correcting it in this routine.
This doesn't give me much confidence in my changes, though!

One map run produced bright pink city icons:
It turns out I had that color in the list of possible icon colors; I'm not exactly sure why.  Anyway, it's not in there any more!

Speaking of city icons:
I like the look of these icons, with the grey buildings, colored roofs and a heavy outline.  But the icons for Omke and Peban have a problem.  It may not be entirely obvious, but the city wall does not extend far enough to the right to cover all the city buildings.

The reason is (I think) fairly straightforward.  I'm finding the leftmost and rightmost points for the wall by looking at the first and last building generated.  However, in some cases the last building generated might not be the furthest to the right.  Buildings can also have secondary buildings, so I need to check all of those as well.  That should fix the problem (although I forgot to save the random seed for the above map, so I can't regenerate it to check).

During my testing I generated this map:
The first thing to notice is that the ocean illustration in the center of the map is not getting masked properly; the underlying ocean lines are showing through.  For various reasons there's a complicated hierarchy of layers in the ocean; it looks like the problem here is that the correct layers are not getting masked.
That fixes the illustration problem.  However, I don't much like the look of the ocean lines in this map.  They really dominate the map when they should be more subtle.  This is an intentional style, modeled after this example map:
I've never really loved this effect, especially when (as in the example above) it is so obvious that it takes over the whole map.  It occurred to me that it might be better if the lines faded away as they got farther from shore, so I added that as an option:
That actually turns out to be a cool effect, giving the sense of ocean depth.

Here's an excerpt of mountains from a map:
There's something odd going on with a few of these mountains.  They're very narrow and tall.  Mostly they're also short, but there's one (which starts behind the “I" in the city name) that's also quite tall.  This doesn't happen very often, and not on every map, so it's probably a problem with the random parameters for the mountains creating some undesirable combination.

However, after some debugging I realize it's not a problem with the mountain parameters, but instead a case where the union of two mountains fails.  The mountain generator merges two mountains in various places.  One example is where it adds a little “rubble pile" to the base of the mountain.  It does this by creating a very small simple mountain (the rubble) that partly overlaps with the real mountain, and then takes the union of the two mountains.  In this case that union was failing and returning the small spiky remains.

I've chronicled my problems with polygon union on this blog before.  It's hard to find a polygon union in Javascript that is (1) correct, (2) fast, and (3) easy to use.  And to be fair, I've made matters worse for myself by assuming that a union of two polygons should return a single polygon.  I only use union in cases where that *should* be true, but occasionally it isn't true either because the union algorithm is wrong or I've used it incorrectly.  At any rate, my general approach when a union returns multiple polygons is to keep the biggest and throw away the rest.  In this case, that logic was broken as a result of some other changes, so I had to go back in and reinstate the correct logic.

Another long-term struggle for me has been getting Dragons Abound to reliably generate the same map over again.  The major problem, of course, is that Dragons Abound makes a lot of random choices when generating a map, so it's unlikely to make the same choices twice in a row.  But of course, computers don't actually use random numbers, they use pseudorandom numbers -- numbers that look random but are actually generated in a specific sequence.  So if you want to get the same sequence of random numbers a second time, you just have to take care to start in the same spot.

Javascript doesn't actually provide this capability, but it's easy to find a replacement random number generator that does.  With this kind of random number generator, you can feed in a starting point (called a “seed") and get back the same sequence every time.  So swapping this in for the usual Javascript random number went most of the way to being able to recreate a map.  Just feed in the same seed and get the same map.

Except it didn't quite work that way.  There would always be a few small differences.  I finally got tired enough of this that I spent a few days tracking down the problem(s).

One thing I discovered fairly quickly was that in a number of spots I was using d3.randomNormal instead of the usual random number generator.  The usual random number generator provides uniformly distributed numbers, usually from 0 to 1.  This means you're just as likely to get any number between 0 and 1 as any other number.  But in many cases, you don't really want a uniform distribution.  If you're randomly generating the heights of men from (say) 4 feet tall to 7 feet tall, you don't want as many 4 foot tall as 6 foot tall men.  You want a bell shape curve centered over the median height, so that you get a lot of 5' 8" tall men and very few 7' tall men.  This is a “normal" distribution, and that's the sort of distribution that d3.randomNormal provides.  But the way I was using it, d3.randomNormal was still using the default Javascript random number generator, and so its output wasn't repeatable.

There are a couple of ways to fix that problem.  A simple random normal generator isn't hard to write, but as it turns out d3 provides a way to specify the source random number generator used by d3.randomNormal.  By telling d3.randomNormal to use the seeded random number generator, it becomes repeatable.

However, there still remained some areas where the map was diverging the second time I generated it.  After much debugging, I figured out that there were some changes in the parameters that were carrying over from the first run into the next run.  The reasons why are complex and not particularly interesting, but essentially, the first time through Dragons Abound was making some choices -- say, to fill region labels with white -- and that choice was being remembered in the second run.  That by itself was okay -- I wanted the program to make the same choices! -- but it also meant that the program used one less random number the second time around, because it didn't need one to make that choice.  And now the sequence was off by one random number.  The solution in that case was to be a little more careful not to carry over any parameters from the first run to the next run.

However, that still didn't fix everything -- specifically, I was still getting different place names on different runs.  The problem in this case was that the tool I am using to generate place names -- RiTa -- was somehow still using the default random number generator.  Logically, it should have been using the new seeded random number generator, but debugging confirmed that it was still using the default generator.  I'm not exactly sure why -- probably some kind of subtle scoping or optimization problem.  The solution was to modify the RiTa code to allow me to provide the random number generator to use.  Then I could pass in the seeded random number generator and force RiTa to use that.

Now I could run the program twice in a row and get the exact same output.  But when I reloaded the page and ran it again, I still get slightly different output.  (But repeatable!)

Several *days* of debugging later I finally trace the difference to part of the mountain creation code that is adding ridge lines.  Eventually I realized (duh!) that the noise generator being used by this code is being initialized as a global.  In Javascript (as in most languages) there is no guarantee about the order of initialization, so apparently the noise generator was getting initialized (which uses some random numbers) *before* the seeded random number generator replaced the default random number generator.

The fix is just to initialize it as part of the regular code flow.  And that -- finally -- is the last non-repeatable element of the code.  Now I get exactly the same output from any particular seed no matter when I load or run the code.

Although at this point I don't recall exactly why this was so important to me :-)

One improvement I've been meaning to get to for some time is to add the ocean illustrations to the label placement routine, so that they can “float" to a better position if the original placement has a problem.  The original placement routine is pretty good, so it's hard to find really serious problems, but here's a mild example;
Here's the label for “Latun's Village" ends up sticking out towards the ocean illustration.  A better placement would move the illustration away from the label to create better separation.

To do this, I need to turn the image into a label and let the label placement routine try to find a better location.  This isn't too difficult; I just turn the image into a label and make sure it has the criteria to maximize the distance to the nearest other label:
Here the black box shows the original location of the label and the purple box the final location.  The Illustration has moved away from the city label.  Unfortunately, this has placed it pretty close to “Ta Island".  This can be improved by tweaking the criteria for this label, but the real solution is to make the ocean illustration also want to stay away from features (like the island) as much as other labels.  That's not a criteria I currently use but it isn't too hard to add.
And now the illustration has pushed up and away from the island to the lower right.  It isn't precisely centered because it has some other criteria to fulfill, but this placement is better than the original placement.

Floating the ocean illustration immediately causes another problem:
The ocean illustration has floated under the compass rose.

In this image I've added green boxes for all the features that the ocean illustration should be trying to avoid.  You can see there's no green box around the compass rose.  There's actually supposed to be, but the code for it is broken.  That error was never apparent before because there's not been anything in the ocean to clash with the compass rose.  But now that the ocean illustration can float around it can happen.  

The solution is to fix the compass rose code to mark it correctly as a map feature to avoid:
Now the illustration has avoided the compass rose.  By the way, the compass rose box looks “too big" because the rose is actually a square image that is slightly rotated.  The slight rotation keeps the rose from looking too rigid and artificial, but it also causes the bounding box to be bigger.  (If the bounding box itself was rotated it would be smaller, but the bounding boxes are aligned to the axes.)

Tuesday, August 14, 2018

The Naming of Places (Part 12): Map Interaction

As much as I'd like Dragons Abound to produce perfect maps every time, that's not a realistic goal.  Procedural generation can either be very complex and limited and get it right all the time, or be less complex and more creative and make some mistakes.  I prefer the latter approach.  This is particularly evident in naming, where I'd have to implement true “artificial intelligence" with a deep understanding of language, culture and history to have a program that created good names all the time.  For the most part, when Dragons Abound makes a poor choice, I'm happy to hit the “Generate" button again and just create a new map.  But there are lots of names on the Dragons Abound maps, so it's likely that one or two of them on every map are clunkers.  If I like a map it would be nice to just re-generate those names and keep the rest of the map.

One possibility is to import the map into an editor like Inkscape and manually fix the names.  I've always imagined this as the final stage for Dragons Abound maps that were going to be used for something (like a FRP campaign).  Bring the map into an editor and clean up any rough edges, tweak areas that don't look quite right, etc.  (Whether Inkscape can handle a Dragons Abound map is another question.)  But that's a bit cumbersome for day-to-day use.

Another possibility is to do something through the program parameters.  I have a vague intention to implement an option to read in feature names from a file (or user input), as a way to let a user provide the names for the map.  That's rather a lot of work for a marginal feature, and has some other drawbacks.  I could implement some sort of ad-hoc override, but again it seems like a lot of work that would probably end up being very finicky to use.

What I'd really like to be able to to do is just click on a name on a finished map and have the program generate a new name.  One drawback with this approach is that the new name might not fit onto the map in the same spot as the old name, e.g., if I replace the name “Fu" with “GenericNameVille" the new name might end up overlapping something else on the map.  So I'll need some way to deal with that problem, too.

Unlike Azgarr, whose map has always been interactiveDragons Abound has always been a batch process -- you click “Generate" and some time later a completed but static map pops up.  So adding interactivity requires some new framework.  Happily, Dragons Abound uses the d3js library, and d3js is really intended for interactive graphics, so it has built-in support for interacting with SVG.  Specifically, it has support for drag and drop, and pan and zoom.  For this effort, I'll be using drag and drop.

The d3js support for drag and drop is illustrated in this example, which I will try to embed here:


If this worked, you should be able to click on the circles and drag them around.

You can follow the link above to see the details of the code, but the basic notion is that I will attach an “event handler" to the SVG elements I want to be interactive (in this case, the text labels) and the event handler will then be called when certain events happen -- specifically when the user clicks on the element, drags the element around, and releases the mouse.  As it turns out, it's actually easier to implement “drag a label around the map" than “click for a new name" so will implement that first.  Drag & drop is pretty useful even without “click for a new name" because there's often one or two labels on the map that I'd like to tweak into a new position.

Every SVG element has a position, so to drag an element around the map, all I have to do is update the position of the element to be the position of the mouse.  The browser will take care of redrawing the element in its new position.  So as the mouse moves around and generates “drag" events, the event handler will change the position of the SVG element, the browser will redraw the element in the new position, and the element will follow the mouse around.  The event handler code to do this isn't much more than this:
function dragged(d) {
  d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y);
}
This takes the element that was clicked upon (“d3.select(this)") and sets its current position (cx, cy) to be the current position of the mouse (d3.event.x, d3.event.y).

Of course, it isn't quite that simple in practice.

One problem is that the position of a label is the center of the label, but the user can click anywhere on the label to select it.  So if I click (say) near the end of the label, the above code immediately updates the center of the label to be the position of the mouse -- so the label “jumps" to center itself under the mouse.  That's a little jarring.  The solution is to use the relative mouse movement -- if the use clicks and drags 15 pixels left, the label should move 15 pixels left of its current position, regardless of where on the label the user clicked.  As it turns out, the drag event also has the relative move (d3.event.dx, d3.event.dy) so this is easily implemented.

A more substantial problem is that the labels in Dragons Abound are not just simple SVG text elements.  Each label is actually three elements: the text element, a mask element, and a halo element.  This is evident when a label overlaps another map element:
You can see the masking and halo effects particularly at the right end of the name where it overlaps the city icon.  To make matters worse, because SVG doesn't support multi-line text, multi-line labels are actually collections of single line labels.  So drag and drop on labels has to move all of these different elements appropriately.  This is mostly just bookkeeping, but does complicate the code.

(Technical details:  The drag event handler only has access to the element clicked upon and the event itself.  So how can the handler access the elements that weren't clicked upon, such as the mask and the other elements in a multi-line label?  The answer is to create a specific drag event handler for each label as a Javascript closure that has access to all the elements of the label.)

Another problem is that some of the Dragons Abound labels are curved, which means they are set to follow an (invisible) SVG path element.  Changing the position of these elements only slides them along the path.  To move the label to a new position requires moving the underlying path.  That's harder than moving a simple element, so I'm going to leave that for another day!

I discovered a problem of another sort when my initial implementation didn't work at all.  None of the elements were responding to events.  Eventually I figured out that the map had a large (mostly transparent) image element covering the entire map (to add a paper texture to the map).  Clicking was selecting this element.  I had assumed that any element without an event handler would just ignore mouse clicks, but apparently that's not the case.  To keep that element from eating mouse events, you need to set the CSS pointer-events property to “none."

After working through those problems:
Responsiveness is not very good -- I assume because the complexity of the SVG makes the repainting of the label as it moves slow -- but it certainly works well enough!

Now let me get to the original point of all this -- to rename the label on a mouse click.  To distinguish renaming from dragging, I'll check to see whether the label has moved at all between the mouse down and the mouse up.  If it hasn't, then I can assume it was a mouse click indicating a renaming.

The first step is to create event handlers for “mouse down" and “mouse up" and get those working.  To start, I'll create a simple handler that turns the label outline red on mouse down and back to black on mouse up:
The next step is to keep track of the position of the label at the start and stop and see if I can distinguish a drag from a click.  Here I'm trying to turn the label red on a click:
This isn't completely obvious, but here I first drag and drop the label, and then I click on the label.  So it looks like that is working.

Now I have to see if I can replace the text when I detect a click.  Here's a test just replacing the label with a test string:
All that remains then is to call the proper name generator to create a new name:
Note that the new label is centered at the same spot as the original label.  Looks pretty good, but looking closely the “halo" (white blurred text behind the main text) looks like it isn't changing.  Poking through the code, I realize that I'm generating a new name for each of the three parts of the label.  Oops!  Better to generate one new name and use it three times:
So that works pretty well.  Except ... it doesn't really work for multi-line labels.  Suppose, for example, that I have a two line label “Serenity/Bay" and I generate a new name which is “Happy/Ocelot/Bay."  What do I do with the extra line?  And it's actually worse than that, because once I create a multi-line label, it's hard to know which SVG text elements belong to which line.

So back to the drawing board.  For the multi-line labels, the only approach that doesn't involve a lot of complicated bookkeeping is to delete the old label and replace it with a new label.  This is a little mind-bending --  the label creation function creates a label with an event handler that deletes the label and then calls the same label creation function to create a new label with an event handler that deletes the label...
But somehow it all works.  So now I have the ability to re-generate labels I don't like, and shift around labels that don't get placed perfectly.  I'm still no Azgarr, but a little bit of interactive functionality like this is very helpful for “tweaking" a map into shape.


Monday, August 6, 2018

The Naming of Places (Part 11): Regions, Rivers and Islands

If you've read the previous ten parts of this series, you probably have a pretty good sense of my naming approach at this point.  So I'm just going to summarize and hit the highlights for naming regions, rivers and islands.

The Dragons Abound maps are divided up into regions but I've been purposely vague about what these regions represent.  The Dragons Abound maps are “regional" meaning (roughly) smaller than a continent, so these regions could represent countries or they could be some smaller subdivisions of a country, like states or dukedoms, etc.  I'd like to have the option to name regions as countries, as parts within a country, or ambiguously.

In the real world, most country names have been around for a long time.  Even if they were originally descriptive, we now treat them as proper names, e.g., the Netherlands is literally “the Low Lands" but we just think of that as a name.  There are a number of suffixes that signify a country name, such as “-land" (e.g., England, Ireland, Holland, etc.) and “-ia" (e.g., Albania, Slovakia and the related Germany, Hungary, etc.).  There are also some countries like “The United States of America" that have political descriptive names.  A straightforward approach to naming regions as countries is to generate a proper name with a country suffix.  I can use Martin O'Leary's original name generator to create the proper name, and then just tack on a suffix.  That gets me names like these:
Klinland
Mintripland
Shaminia
Tripchia
Duland
Lilinia
Putitia
Shaland
Tluntaimland
Plishia
As usual, Martin's generator creates the occasional tongue-twister (“Tluntaim"?) but overall this produces pretty acceptable country names.

Let's move on to naming islands.  In English, the synonyms for “island" aren't used very frequently.  In my corpus:
island: 20459
rock: 1800
islands: 1169
key: 922
rocks: 320
ledge: 178
keys: 165
isle: 118
towhead: 115
atoll: 90
islet: 50
holm: 43
islets: 38
cay: 36
bar: 34
head: 32
tump: 26
isles: 18
lump: 14
nubble: 12
lumps: 12
Some of these are pretty specialized words.  A “towhead" is “A sandbar or low-lying alluvial island in a river, especially one with a stand of trees."  Still, I can probably use these occasionally.

The actual names of islands overlap extensively with the names of bays and points; like those objects they have characteristics of both land and sea.  Looking at some of the names listed in my corpus of real world island names shows all the usual suspects:
Aaron Island
Abalone Island
Abbess Island
Abbey Island
Abbot Rock
Abernethy Island
Abnecotants Island
Abraham Islet
Acorn Rock
You have people's names, names of sea creatures, titles, etc.  (Note “Abnecotants" which I probably would have rejected as awkward and unbelievable if it was generated.)  Looking at the adjectives used in real-world island names shows many of the same adjectives I already have for bays and points:
big: 401
long: 184
black: 139
green: 131
middle: 129
great: 105
white: 104
round: 91
outer: 63
old: 62
upper: 59
grassy: 55
lower: 55
inner: 52
flat: 51
lone: 47
high: 45
brown: 34
blue: 34
sandy: 33
Lots of physical descriptors, colors, and features.  I'll add all the specific adjectives and nouns from my corpus to the same general rules as naming points and bays.  That gives me names like these:
Meyo Island
Sea Urchin Island
Tight Island
Gul Nubble
Gobu Rock
Neler Island
Holy Island
Te Island
Heavenly Key
Se Island
These names also go through the rules for modification.  In this case “Gul Nubble" was originally “Gull Nubble" and “Neler Island" was shortened from “Nedeller Island."

The first thing to notice about river names is that there are a *lot* of them in my corpus.  There are about 233,000 named rivers in the U.S. in one of my sources.  And many synonyms for rivers:
creek: 182794
branch: 51903
brook: 17379
river: 14006
fork: 6861
slough: 4285
bayou: 4002
wash: 3811
stream: 1942
drain: 1815
swamp: 1328
arroyo: 639
bottom: 531
gut: 438
prong: 411
ditch: 395
outlet: 251
water: 237
chute: 208
gully: 205
cutoff: 171
inlet: 167
coulee: 159
hole: 139
bottoms: 119
channel: 103
elbow: 87
cut-off: 84
pup: 73
brake: 67
gulch: 50
slu: 49
beck: 38
swale: 27
canal: 26
stringer: 25
head: 24
There are a few synonyms in that list that are new to me.  “Pup" is an interesting one -- the definition of pup as a stream doesn't appear in any of the online dictionaries I checked, but it is clearly used that way in my corpus of names.

One thing to note about this list is that most of these synonyms refer to smaller rivers.  Depending on the scale of a map, these smaller rivers might not even appear on the map.  So the distributions here can't be blindly used; probably on my maps almost every river that gets labeled should be a “river."  But it does suggest that I could label rivers based upon length and use the “creek" synonyms for the shorter ones.

Taking a look at a selection of river names you see the typical types of names:
Babmore Branch
Babocomari River
Baboon Creek
Baboosic Brook
Baboquivari Wash
Babs Branch
Baby Creek
Baby King Creek
Baby Wagon Creek
Babyfoot Creek
Babyhead Creek
Note that there are a couple of tongue-twister names.  With “Baby", you see exactly the sort of name formations I've done previously -- the word as a single adjective (“Baby Creek"), as an adjective with a noun (“Baby King Creek"), and then with the two compressed together (“Babyfoot Creek").

An interesting question is whether the names of the larger rivers follow a different pattern than the names of the smaller rivers.  We might expect the smaller rivers to have more adventurous names.  Here are the first 20 rivers that start with B:
Babel River
Babocomari River
Back Fork Elk River
Back River
Bad Axe River
Bad River
Bagaduce River
Baker Branch Saint John River
Baker River
Bald River
Baldwin River
Ball Club River
Ballinderry River
Ballybannan River
Ballygomartin River
Baltimore River
Banister River
Bannahassee River
Bantam River
Baptism River
There are still a variety of names here, but it does tend more towards proper names (e.g., “Baltimore") than descriptive names (e.g., “Bad Axe"), and that's what I observe in general for river names.

The adjectives have the usual mix of physical descriptors, colors, and features:
big: 4345
dry: 3248
middle: 2950
long: 1713
black: 1322
white: 1216
rocky: 1058
red: 1026
right: 1011
hollow: 974
sandy: 897
clear: 889
flat: 862
old: 789
crooked: 754
blue: 733
upper: 704
deep: 691
brushy: 667
lower: 633
Not unsurprisingly, the water adjectives (e.g., “clear", “deep") are used for rivers.  So the names of rivers ends up looking a lot like the names of bays, although I use a different distribution of the types of names for rivers and creeks:
Mimigon River
Gongoe River
(Shortened Gonsliggoe to Gongoe)
Gamslig River
Ideal River
Galon River
(Shortened Galleon to Galon)
Billet River
Bagnio River
Salvation River
Thole River
Shosligglog River
Pabo's Branch
Frosty Frog Creek
Sapphire Creek
Wild Brook
Chainmaker's Creek
Thegian Gloggo's Creek
(Shortened Theologian to Thegian)
Gomomnog Creek
Plolpot Creek
Mioer's Creek
Gloggo's Creek
You can see that the rivers have a lot of proper names, while the creeks have more descriptive names.  The shortening rules have also kicked in to twist a few names.

Finally (finally!) here's an example map with all new name invention (click through for larger version):
This is a map with a lot of labels and some label placement challenges, but overall I think this is pretty good.  There are a number of very nice names on the map -- Oyster Inlet, Drydock Point, Steinston, Riverwood, Pitsville / Old Pitsville, etc.  There are a few clunkers -- I don't particularly like Nunse Village or Roderle, but they're not so terrible they ruin the map.

I've been working a bit on improving the placement of river labels, and the river label placements on this map are very good except for Logrhead River in the lower right that overlaps the city icon (and comes between the icon and the city name).  As far as the rest of the labeling goes, the top of the map is too busy, and a human labeler would probably have moved Ketlom Island to the bottom of the island (where it would connect better with the namesake Ketlom city as well).  It might be worth considering a heuristic that checks how dense the labels are in an area and eliminates some of the minor labels if it is too dense.  But the placement of the ocean illustration is perfect.

By the way, the map above is a random invented style.  Here's the same map in a more traditional fantasy style, and with different names as a result:
A nice thing on this map is that you get a little sense of the “native" language in the names like Dukbip, Blibduk and Blibton.  And I love the way the tall snow-covered mountain at the bottom is in front of the border line.  I don't think I've ever seen that happen before, but it's a great effect.