Public
Edited
Jun 10
Importers
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
generators = [...Array(100)].map(_ => [
size[0] * Math.random(),
size[1] * Math.random()
])
Insert cell
Insert cell
Insert cell
Insert cell
sampleAlgo = function(array2D, range) {
const { x, y } = this.thread;
const [min, max] = range;
const h = array2D[y][x];
return Math.min(max, Math.max(min, h));
}
Insert cell
viewof sampling = {
const [W, H] = size;
const [W2, H2] = [W / 2, H / 2];
const nbGens = generators.length;
const { bar, set, dispatch } = viewofProgressBar(`Generating sampling`, W);

const S = [];
let [maxD, maxN] = [-Infinity, -Infinity];

for (let i = 0; i < W; i++) {
S[i] = [];

for (let j = 0; j < H; j++) {
let [v, d, n] = [0, [0, 0, +Infinity], 0];

for (let g = 0; g < nbGens; g++) {
const [X, Y] = generators[g];
let [dx, dy] = [X - i, Y - j];
dx = dx > W2 ? dx - W : dx < -W2 ? dx + W : dx;
dy = dy > H2 ? dy - H : dy < -H2 ? dy + H : dy;
const d2 = dx * dx + dy * dy;
if (d2 < d[2]) {
d = [dx, dy, d2];
v = g;
}
n += Math.exp((-d2 * 100) / (W * H));
}

S[i][j] = [v, d, n];
maxD = Math.max(maxD, d[2]);
maxN = Math.max(maxN, n);
}

if (i % 10 == 0) {
set(i);
yield bar;
}
}

set(W);
dispatch({ S, maxD, maxN });
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mapper = ([x, y], s) => {
const mod = (val, size) => {
const m = (val | 0) % size;
return m < 0 ? m + size : m;
};
return [mod(x * s, size[0]), mod(y * s, size[1])];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
shades = ([r, g, b]) => (s = 1) => [r*s|0, g*s|0, b*s|0]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
color = ((...colors) => s => colors[s%1*colors.length|0])(BLUE, SAND, GREEN, GREY)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
generateRow = (x, y_, dy, palette, params) => {
// 1st pass: compute n×[height, color] from shader
const { shader, defColor } = params;
const min = ([h, c]) => (h < 0 ? [0, defColor] : [h, c]);
const vs = y_.map(y => min(shader([x, y])));

// 2nd pass: apply shading+shadowing
const { shadow, ambient, sunVector } = params.shading;
const [ys, zs] = sunVector;
const sp = -zs / ys;
let max = 0;
const N = y_.length;
const K = zs * 2 * dy;
const heightRow = vs.map(([h]) => h);
const colorRow = vs.map(([h, c], y) => {
// shading
const [y1, y2] = [(y + N - 1) % N, (y + 1) % N];
const dh = vs[y1][0] - vs[y2][0];
const sc = ys * dh + K;
let s = sc > 0 ? 0.7 * (sc / Math.hypot(2 * dy, dh)) : 0;

// shadowing
/*if (h >= max) max = h;
else s *= 1 - shadow;
max -= sp;*/
[max, s] = h >= max ? [h - sp, s] : [max - sp, s * (1 - shadow)];

return palette.add(shades(c)(s + ambient));
});
return { heightRow, colorRow };
}
Insert cell
Insert cell
Insert cell
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);
}
}
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tree3 = (p, s) => [
((1 + V(p, 0.5 * s) / 4 - 30 * D2(p, 0.5 * s) + N(p, 23 * s) / 8) * 50) / s,
green(0.2 + N(p, 0.5 * s) / 3)
]
Insert cell
slice = (a, b) => x => Math.min(b - a, Math.max(0, x - a)) / (b - a)
Insert cell
sl = slice(0.3, 0.4)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Sea = (f, level) => p => {
const [h, c] = f(p)
return level >= h ? [0, BLUE] : [h-level, c]
}
Insert cell
Scale = (f, s) => p => {
const [h, c] = f(p)
return [h*s, c]
}
Insert cell
cypress2 = (p, s = 1) => [
Math.max(0, ((0.4 + V(p, s) - 60 * D2(p, s)) * 80) / s),
green(0.2)
]
Insert cell
fir2 = (p, s = 1) => [((2 * V(p, s) - 5 * D(p, s)) * 80) / s, green(0.3)]
Insert cell
tree2 = (p, s = 1) => [
((5 + 3 * V(p, s) + N(p, s * 15) - 10 * D2(p, s)) * 10) / s,
green(0.1 + V(p, s) / 7)
]
//[(50 + 28 * V(p, s) - 6 * V(p, s * 7) - D2(p, s) * 100) / s, green(0.2 + V(p, s) / 10)]
Insert cell
bush2 = (p, s = 1) => [
(30 + 28 * V(p, s) - 10 * V(p, s * 7) - D2(p, s) * 120) / s,
green(0.1 + V(p, s) / 8)
]
Insert cell
buildings2 = (p, s = 1) => [(50 * V(p, s)) / s /*-10*V(p, s*2)/s*/, grey(0.6)]
Insert cell
terrain2 = (p, s = 1) => [(7*N(p, s) + N(p, s*4))*10/s + N(p, s*30)*2, sand(0.7)]
Insert cell
waterlilys = (p, s) => (D2(p, 15) < 0.1 ? [0.01, green(0.3)] : [0])
Insert cell
rocks2 = (p, s = 1) => [
((1 + V(p, s) - D2(p, s) * 10) * 15) / s,
grey((4 + N(p, s) + N(p, s * 4)) / 10)
]
Insert cell
buildings = Scale(p => [V(p, 3), SAND], 15)
Insert cell
hills_ = p => [150*N(p, 0.5) + 20*N(p, 2), green(0.5)]
Insert cell
Sea2 = ([h, c], level) => level >= h ? [0, blue(0.8)] : [h-level, c]
Insert cell
terrain3 = (p, s = 1, w = 0) => {
const h = (80 * N(p, s) + 10 * N(p, s * 4) + N(p, s * 16)) / s;
return [h - w * s, sand(0.7)];
}
Insert cell
test = (p, s = 1) => {
const terrain = (p) => terrain3(p, s / 2, 100);
const g4 = G(p, s * 4);
const v4 = V(g4, s * 2);
const g16 = G(p, s * 16);
const v16 = V(g16, s * 2);
const h = terrain(g16)[0];
const h4 = terrain(G(p, s * 4))[0];
let hills = terrain(p);

// Vegetation / rocks
let f = [0];
if (params.rocks)
if (h > 30 && Math.abs(hills[0] - terrain(g4)[0]) > 4)
f = Max(f, rocks2(p, s * 20));
if (params.grass) if (h > 1) f = Max(f, short_grass(p));
if (params.bushes)
if (h > -1 && h < 20 && v16 > 0.4) f = Max(f, bush2(p, s * 16));
if (params.hardwood)
if (h4 > 2 && h4 < 80 && sl(N(G(p, 4), 1)) ** 0.3 > 0) {
f = Max(f, [
(7 + 7 * V(p, 4) + 3 * N(p, 50) - D2(p, 4) * 25) * sl(N(p, 1)) ** 0.3,
green(0.15 + V(p, 4) / 8)
]);
}
if (params.cypress)
if (h > 70 && h < 90 && v4 > 0.8) f = Max(f, cypress2(p, s * 4));
if (params.rocks2)
if (h > -2 && h < 5 && V(G(p, s * 10), s * 2) > 0.9)
f = Max(f, rocks2(p, s * 10));

Add(hills, f);

// Water
let w = water(p);
if (params.waterlilies)
if (h > -5 && h < 0 && v16 > 0.8) w = Add(w, waterlilys(p, s));

return Max(w, hills);
}
Insert cell
Insert cell
Insert cell
exploreNow((p) => test(p, 1))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
/*multiview(size, [
p => [110 * quad(N(p, 0.5)) + 10 * N(p, 3), SAND],
p => [110 * cubic(N(p, 0.5)) + 10 * N(p, 3), SAND],
p => [110 * quart(N(p, 0.5)) + 10 * N(p, 3), SAND],
p => [110 * quint(N(p, 0.5)) + 10 * N(p, 3), SAND]
])*/
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
water = (p) => [0, blue(0.1 + N(p) / 4 + N(p, 4) / 8)] // [0, 2, Math.exp(h/5)*(1-v(6).I/5)]
Insert cell
Insert cell
Insert cell
short_grass = p => [0.1, green(0.3 + N(p) / 8)]
Insert cell
tall_grass = p => [
2 * V(p, 5) + V(p, 20) - D2(p, 20),
green(0.3 + V(p, 3) / 15)
]
Insert cell
Insert cell
Insert cell
rocks = p => [3+3*V(p, 5)-D2(p, 5)*20, grey(0.3+V(p, 5)/8+V(p, 20)/10)]
Insert cell
rocky = p => L(0, [], sands(p), rocks(p))
Insert cell
Insert cell
city = p => [100*V(p)**4+20*V(p, 1.2)-10, grey(0.7)]
Insert cell
city2 = p => {
const [dx, dy] = Dxy(p);
const l = 0.05;
const h = V(p);
return [Math.abs(dx) < l && Math.abs(dy) < l ? 100 * h * h : -1, grey(0.7)];
}
Insert cell
trees = p => [
5 + 7 * V(p, 4) + 4 * N(p, 30) - D2(p, 4) * 25,
green(0.15 + V(p, 4) / 10)
]
Insert cell
fir = p => [
15 + 15 * V(p, 4) - Math.sqrt(D2(p, 4)) * 70,
green(0.1 + V(p, 2) / 10)
]
Insert cell
sands = p => [N(p, 2) / 10 + N(p, 10) / 20, sand(1)]
Insert cell
soil = p => [N(p, 2)/4, sand(0.2-V(p, 8)/20)]
Insert cell
snow = p => [N(p), grey(1-V(p, 2)/15)]
Insert cell
terrain = (p, f) => f * (10 * N(p, 0.5) + N(p, 3) / 2 - 3) /*-3.5*/
Insert cell
grassland = p => {
const h4 = terrain(G(p, 6), 8);
const s = sands(p);
return L(
terrain(p, 8),
water(p),
...(h4 > 15 ? [trees(p)] : h4 > 5 ? [tall_grass(p), s] : [s])
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
largeView = async function*(shader, params) {
const dims = [width, (width * 0.6) | 0];
const context = DOM.context2d(...dims, 1);
const canvas = context.canvas;
const never = () => new Promise(resolve => {});
const once = async function*() {
await asyncVisible(canvas);
yield true;
await never();
};
const div = html`<div></div>`;
div.append(canvas);
yield div;
const data_ = generate(shader, params, once());
orbitalRenderer(data_(), context);
}
Insert cell
sideview = async function* (shader, text) {
const size = 400;
const block = html`<div></div>`;
block.style.height = `${size * 0.6}px`;
const div = html`<div></div>`;
div.style.float = "left";
Object.assign(div.style, { marginRight: "20px" });
block.append(div);
text.classList.add("sidetext");
block.append(text);
yield block;

const dims = [size, (size * 0.6) | 0];
const context = DOM.context2d(...dims, 1);
const canvas = context.canvas;
div.append(canvas);

const params = { center: [0, 0], size: [512, 512], resolution: 512 };
const data_ = generate(shader, params, asyncVisible(canvas));
orbitalRenderer(data_(), context);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more