2

Question: What in the world is this piece of code doing?

Also: Is the way 'w' is being used some sort of existing algorithm? I'm trying to figure out the intent of the function, or at least describe what sorts of numbers it produces.

Context: I'm looking at Martin O'Leary's "Fantasy Map Generation" code - full source here, which in short summary generates fantasy maps on the canvas. There is some insightful explanations of how the higher level process works in a blog post, but this is too low level to get any coverage there. There is a particular function called 'rnorm' that gets used in a couple of places, and I'm lost at how it works. I've included it below, followed by a couple of instances where it comes up for some context. Any help on what this thing is doing would be great!

var rnorm = (function() {
    var z2 = null;

    function rnorm() {
        if (z2 != null) {
            var tmp = z2;
            z2 = null;
            return tmp;
        }
        var x1 = 0;
        var x2 = 0;
        var w = 2.0;
        while (w >= 1) {
            x1 = runif(-1, 1);
            x2 = runif(-1, 1);
            w = x1 * x1 + x2 * x2;
        }
        w = Math.sqrt(-2 * Math.log(w) / w);
        z2 = x2 * w;
        return x1 * w;
    }
    return rnorm;
})();

runif(), which is called in the code above, is a short function that generates a random number between two given values

function runif(lo, hi) {
    return lo + Math.random() * (hi - lo);
}

This code is used to produce random vectors (actually the only place it's used during the generation process) -

function randomVector(scale) {
return [scale * rnorm(), scale * rnorm()];
}

But I think it's doing more than that because the following, when provided a direction of 'randomVector(4),' produces a gradual slope over the entire mesh heightmap: EDIT: no, it actually is having no effect on the gradual slope. That comes from some sneakyness using the fact that one side of the map is 0,0, and the other side of the map is width,height, which creates numbers that gradually increase.

function slope(mesh, direction) {
    return mesh.map(function (x) {
        return x[0] * direction[0] + x[1] * direction[1];
    });
}

Let me know if there's anything else I should be providing. This is my first question here, so I may be a little soft on conventions.

1 Answers1

2

I think it's horrible code. It appears to create a pair of values, z1 and z2, but instead of putting them in a tuple and returning that it returns z1 and on every second call the corresponding z2 value. I have no idea why they'd do such a thing, my only guess would be to avoid allocation of objects and make usage syntactically more convenient.

It should be simplified to

function rnorm() {
    var x1 = 0;
    var x2 = 0;
    var w = 2.0;
    while (w >= 1) {
        x1 = runif(-1, 1);
        x2 = runif(-1, 1);
        w = x1 * x1 + x2 * x2;
    }
    w = Math.sqrt(-2 * Math.log(w) / w);
    return [x1 * w, x2 * w];
}

function randomVector(scale) {
    var [z1, z2] = rnorm();
    return [scale * z1, scale * z2];
}

A modern compiler should be able to avoid array allocation for the returned literal and subsequent destructuring. If it's not, you can do it manually by inlining rnorm in randomVector, especially if this is the only place where it's called anyway.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • That's really interesting! Thank you. Do you have any idea what 'w' is doing? I'm still trying to pick out the intent of what kinds of value pairs this is supposed to be creating – Ben Spalding Nov 22 '17 at 20:47
  • Well the loop chooses some random coordinates inside the unit circle, where `w` is the length of the origin vector squared. I have to admit I have no idea why `Math.sqrt(-2 * Math.log(w) / w)` is used - maybe [its plot](https://www.wolframalpha.com/input/?i=sqrt(-2+*+log(w)+%2F+w)) offers some insight – Bergi Nov 22 '17 at 21:00
  • 2
    This seems to be a basic Muller Transform. https://en.m.wikipedia.org/wiki/Box–Muller_transform – Alberto Schiabel Nov 22 '17 at 21:26
  • @AlbertoSchiabel Ah, thanks. I was confused by the possibility of generating infinite values - but they are very unlikely. – Bergi Nov 22 '17 at 21:43
  • 1
    The corresponding blog post mentions a prior messy python implementation. I don't know if it's the case here, but I've seen some really weird stuff when someone has ported code and inadvertently worked against the grain of the target language. – Pikalek Nov 24 '17 at 03:49