The sizes are wrong. Size 3*3 should zoom to size 5*5, but your code produces 4*4, losing the bottom row and rightmost column.
The formula for the dimensions should be New = Old * 2 - 1; you have New = (Old - 1) * 2
Other than that, good job, it really looks great.
I believe I fixed it. Could you try again?
EDIT:
Wait, something seems wrong now.
The bounds in the for loops are too low, drop the "-1"
I tried that, but now colors from the right can suddenly appear on the left as well.
Ah, that's because you're constructing the chain from the opposite side.
What the GrownGen actually does, it requests the lower data in an area larger by one in each dimension. In order to implement that in your visualiser, you'd need to modify the whole thing to not display the rightmost column and bottom row of the data, but still keep it for the future zoom / smooth operations.
reset() {
this.sizeX = 4;
this.sizeZ = 4;
...
}
zoom() {
let lowStepX = (this.sizeX - 1) * 2;
let lowStepZ = (this.sizeZ - 1) * 2;
let cache = new Uint8Array(lowStepX * lowStepZ);
for (let z = 0; z < this.sizeZ - 1; z++)
{
let idx = (z * 2) * lowStepX;
let PrevZ0 = this.values[z * this.sizeX];
let PrevZ1 = this.values[(z + 1) * this.sizeX];
for (let x = 0; x < this.sizeX - 1; x++)
{
let ValX1Z0 = this.values[x + 1 + z * this.sizeX];
let ValX1Z1 = this.values[x + 1 + (z + 1) * this.sizeX];
cache[idx] = PrevZ0;
cache[idx + lowStepX] = this.chooseRandomNumber(PrevZ0, PrevZ1);
cache[idx + 1] = this.chooseRandomNumber(PrevZ0, ValX1Z0);
cache[idx + 1 + lowStepX] = this.chooseRandomNumber(PrevZ0, ValX1Z0, PrevZ1, ValX1Z1);
idx += 2;
PrevZ0 = ValX1Z0;
PrevZ1 = ValX1Z1;
}
}
this.values = cache;
this.sizeX = lowStepX;
this.sizeZ = lowStepZ;
}
visualize(context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
const squareSizeX = canvas.width / (this.sizeX - 1) - g_DistanceBetweenSquares;
const squareSizeY = canvas.height / (this.sizeZ - 1) - g_DistanceBetweenSquares;
for (let x = 0; x < this.sizeX - 1; x++)
{
for (let y = 0; y < this.sizeZ - 1; y++)
{
let renderX = canvas.width / (this.sizeX - 1) * x + g_DistanceBetweenSquares;
let renderY = canvas.height / (this.sizeZ - 1) * y + g_DistanceBetweenSquares;
context.fillStyle = g_Colors[this.values[x + y * this.sizeZ]];
context.fillRect(renderX, renderY, squareSizeX, squareSizeY);
}
}
context.save();
context.globalCompositeOperation = 'difference';
context.fillStyle = 'white';
context.fillText("Size: " + (this.sizeX - 1) + "x" + (this.sizeZ - 1), 5, 10);
context.restore();
}
There are only two hard things in Computer Science: cache invalidation, naming things and off-by-one errors.
It's working perfectly now

Thanks. We could put this in the generator documentation as well.
if you don't have any objections against it I'd be willing to put it in the generator.html. If not, would you like me to replace the existing pictures, or put it somewhere at the back of the
Grown documentation?