generate = function(shader, dims, control) {
const { center, size, resolution: N } = dims;
const [x0, y0] = center;
const [dx, dy] = size;
const array = (size, mapper) => [...Array(size)].map((_, i) => mapper(i));
const map = (size, value) => array(size, _ => array(size, _ => value));
const defColor = sand(0.8);
const sunVector = [-Math.cos(sunElevation), Math.sin(sunElevation)];
const shading = { shadow: 0.8, ambient: 0.3, sunVector };
const params = { shader, shading, defColor };
const step = 32;
const ys = array(N, i => y0 - dy / 2 + i * (dy / N));
return async function*() {
const never = new Promise(_ => null);
let allow = control.next();
let generation = never;
let gdata, rdata, palette;
const nextRow = index =>
new Promise(resolve => {
if (index < N) {
const x = x0 - dx / 2 + index * (dx / N);
const row = generateRow(x, ys, 1, palette, params);
gdata.heightmap[index] = row.heightRow;
gdata.colormap[index] = row.colorRow;
resolve({ value: index + 1 });
}
});
while (true) {
const res = (await Promise.race([allow, generation])).value;
if (typeof res == 'boolean') {
allow = control.next();
if (!res) {
gdata = rdata = null;
generation = never;
} else if (!gdata) {
palette = Palette(1);
const gi = palette.add(defColor);
gdata = { heightmap: map(N, 0), colormap: map(N, gi), palette };
rdata = {
heightmap: { data: gdata.heightmap, ratio: 1 },
colormap: { data: gdata.colormap, palette: palette.colors() }
};
generation = nextRow(0);
}
} else {
if (res % step == 0) yield rdata;
generation = nextRow(res);
}
}
};
}