The idea in plain words

In Sea of Points a noise field decided how close the points could be. Here the points are trees, and what decides is each tree's age: a seedling needs a metre and a half, an old araucaria about seven, the radius of its crown. So the spacing r is no longer a map: it travels with each tree.

The ages are not invented. Field studies of araucaria forests find many young trees and few old ones, and patches where a fire killed everything but the big trees and a new cohort grew after it. The four distributions in the selector are four answers to «where does each tree go?». ✦ IA

What the forests say ✦ IA

  • Many young, few old. In eight plots in Neuquén, about 9,600 seedlings per hectare against 537 adults; sizes fall like a reversed J, and the forest keeps regenerating under its own canopy (Dezzotti et al. 2012).
  • Fire leaves the big ones. After a fire every araucaria under 30 cm of trunk died, the larger ones lived, and the seedlings gathered around the surviving females (Burns 1993).
  • Seeds fall close. The pewen's seeds are heavy and drop near the tree; austral parakeets carry about one in a thousand 5 to 50 m away (Tella et al. 2019).
  • Density. From 81 trees per hectare in open woodland to 1,194 in closed forest (Dezzotti et al. 2012).

From the sources to the code, step by step ✦ IA

  1. Each candidate draws a background age from an exponential: most come out young, a few old. Mean age of the young moves that mean.
  2. The world is split into patches (the cells of a jittered grid, 70 m apart). Some burned, each at its own time. In a burned patch a tree older than the fire lives only if it was already big then (Age needed to survive stands for the 30 cm); the others become a recruit younger than the fire.
  3. The age gives the room the tree claims, its r.
  4. The distribution decides where the trees go and who stays.

Four distributions ✦ IA

  • Chance. Trees anywhere, the same number per area as a Poisson field with that r, but nobody keeps a distance: clumps, gaps and crowns on top of each other.
  • Plantation. Stands planted at one age, in rows 1.46·r apart, each stand turned its own way. No fire: someone planted it. The rows read from far away; that is the point of the comparison.
  • Room (Poisson). Sea of Points' variable r, with one change: the older tree wins a clash, because it was there first. The old keep their distance and the young fill the gaps between them.
  • Around mothers. First the adults, by the same Poisson; then each female drops her young around her, a few metres away, and now and then one flies far with a parakeet. The young do not keep a distance: they grow in clumps, as in the plots.

What was measured ✦ IA

With this page's code (tools/scenes/araucaria-forest-bench.mjs): 10 seeds, a world of 3 × 3 chunks of 40 m (1.44 ha), 3 rounds, fires on.

Distributiontrees / ha (adults)chunked = wholepairs closer than r: inside · seamsms per chunk (Node, PC)
Chance1,468 (18)10/10–10
Plantation619 (69)10/100 · 03
Room (Poisson)790 (51)10/100 · 034 (peaks of 124)
Around mothers253 (54)10/100 · 0 (mothers)4
  • Every distribution gives, chunk by chunk, exactly the forest of the whole world, as in Sea of Points: the halo is 2 · 7 m · 3 rounds.
  • The densities fall inside the range of the plots (81–1,194 per hectare), except Chance, which piles up seedlings because nothing thins them. The adults (age ≥ 0.45, the age of the first cones) are fewer than the 537 of Neuquén: there «adult» is a trunk size, here an age; not calibrated.
  • In Poisson the youngest band (0–0.1) has fewer trees than the next one: the old trees win the clashes and the seedlings near them lose. The reversed J shows from 0.1 on.
  • Generating one araucaria costs 2 to 38 ms, so the scene draws each tree from a cache: 11 age classes × 3 seeds × its crown (one of the five, by chance) × its mature height (30 to 40 m, in steps of 2.5 m), mirrored half the time. The pixel art does not read the scale leaves, so it is generated without them: a pixel-art tree costs 1 to 3 ms. A Poisson chunk costs about 34 ms on a PC; on a phone it is not measured: the legend shows the time of the last chunk.

What it does not do

No lenga, ñirre or coigüe: the forest is araucaria only, although in Chile it shares the slopes with them (coigüe lower, lenga up to the tree line). No relief, so no altitude bands. Patches are straight-edged cells. Ages are not years. None of this is in Islands yet; the proposal is to port the core to C# with the same test: chunked = whole. ✦ IA

How it works ✦ IA

This part walks through the forest's core, src/scripts/araucaria-forest/core.js. It covers:

  • From age to room.
  • Ages with fire.
  • Reusing Sea of Points' rounds with the old tree first.
  • The young around their mothers.

1. From age to room

A log blend, as Sea of Points' radiusAt: equal steps of age multiply the room by equal factors, so the density of the young and the old differ by (7 / 1.5)², about 22 times.

// The room a tree of this age claims: a log blend from rYoung to rOld, reached at age 0.8.
export function radiusFor(age, p) {
  return p.rYoung * Math.pow(p.rOld / p.rYoung, smooth(0, 0.8, age));
}

2. Ages with fire

An exponential draw gives the reversed J; the fire patch rewrites the ages of the trees that would not have survived it.

// The background age: an exponential draw, capped at 1. Most trees are young, a few are old.
const backgroundAge = (u, p) => Math.min(1, -p.youngMean * Math.log(1 - u));

// In a burned patch a tree older than the fire lived only if it was already big then;
// one that died left room for a recruit, younger than the fire.
function naturalAge(x, y, u, w, p) {
  const a = backgroundAge(u, p), since = fireAt(x, y, p);
  if (!since || a <= since) return a;
  return a - since >= p.survive ? a : since * w;
}

3. The old tree first

Sea of Points now exports its rounds as resolve(), so the forest brings its own candidates. The priority puts the age in the top bits: in every clash, the older tree wins.

// Priority: the older tree wins, ties by hash. The oldest tree was there first.
const priority = (age, hash) => ((Math.floor(age * 1023) << 22) | (hash >>> 10)) >>> 0;

// Each candidate: x, y, r, region, priority — then Sea of Points' own rounds.
c.push(x, y, radiusFor(age, p), FOREST, priority(age, hash));
const out = resolve(c, { rMax: p.rOld, rounds: 3, pair: "max", shared: true }, b);

4. The young around their mothers

The mothers are computed over the chunk plus 50 m, the farthest a seed flies, so a chunk also gets the young of mothers that stand in the next one.

// Her young: heavy seeds fall close (a Gaussian of sigma), a few fly with parakeets.
if (h01(hash, m, 0, 802) < p.flight) d = 15 + 35 * h01(hash, m, 0, 803);
else d = Math.min(3, Math.sqrt(-2 * Math.log(1 - 0.999 * h01(hash, m, 0, 804)))) * p.sigma;

La idea en palabras simples

En Mar de puntos un campo de ruido decidía cuánto se podían acercar los puntos. Aquí los puntos son árboles, y lo que decide es la edad de cada uno: una plántula necesita metro y medio; una araucaria vieja, unos siete, el radio de su copa. Así que la separación r ya no es un mapa: viaja con cada árbol.

Las edades no son inventadas. Los estudios de campo en bosques de araucaria encuentran muchos jóvenes y pocos viejos, y parches donde un incendio mató todo menos los árboles grandes y después creció una cohorte nueva. Las cuatro distribuciones del selector son cuatro respuestas a «¿dónde va cada árbol?». ✦ IA

Lo que dicen los bosques ✦ IA

  • Muchos jóvenes, pocos viejos. En ocho parcelas de Neuquén, unas 9.600 plántulas por hectárea frente a 537 adultos; las tallas caen como una jota invertida, y el bosque se sigue regenerando bajo su propio dosel (Dezzotti et al. 2012).
  • El fuego deja a los grandes. Tras un incendio murieron todas las araucarias de menos de 30 cm de tronco, vivieron las mayores, y las plántulas se juntaron alrededor de las hembras que sobrevivieron (Burns 1993).
  • La semilla cae cerca. Los piñones son pesados y caen junto al árbol; las cachañas llevan más o menos uno de cada mil a entre 5 y 50 m (Tella et al. 2019).
  • Densidad. De 81 árboles por hectárea en el bosque abierto a 1.194 en el cerrado (Dezzotti et al. 2012).

De las fuentes al código, paso a paso ✦ IA

  1. Cada candidato saca una edad de fondo de una exponencial: casi todos salen jóvenes y unos pocos viejos. Edad media de los jóvenes mueve esa media.
  2. El mundo se divide en parches (las celdas de una rejilla con jitter, a 70 m). Algunos se quemaron, cada uno en su momento. En un parche quemado, un árbol más viejo que el incendio vive solo si ya era grande entonces (Edad para sobrevivir hace de los 30 cm); los demás pasan a ser un recluta más joven que el incendio.
  3. La edad da el sitio que reclama el árbol, su r.
  4. La distribución decide dónde van los árboles y quién se queda.

Cuatro distribuciones ✦ IA

  • Azar. Árboles en cualquier sitio, los mismos por superficie que un campo de Poisson con ese r, pero nadie guarda distancia: grumos, huecos y copas encimadas.
  • Plantación. Rodales plantados a una sola edad, en hileras separadas 1,46·r, cada rodal girado a su manera. Sin incendios: alguien lo plantó. Las hileras se leen de lejos; para eso está en la comparación.
  • Sitio (Poisson). El r variable de Mar de puntos, con un cambio: en un choque gana el más viejo, porque estaba antes. Los viejos guardan distancia y los jóvenes llenan los huecos entre ellos.
  • Junto a las madres. Primero los adultos, con el mismo Poisson; después cada hembra deja caer sus jóvenes alrededor, a pocos metros, y de vez en cuando uno vuela lejos con una cachaña. Los jóvenes no guardan distancia: crecen en grupos, como en las parcelas.

Lo que se midió ✦ IA

Con el código de esta página (tools/scenes/araucaria-forest-bench.mjs): 10 semillas, un mundo de 3 × 3 chunks de 40 m (1,44 ha), 3 rondas, con incendios.

Distribuciónárboles / ha (adultos)por chunks = enteropares a menos de r: dentro · costurasms por chunk (Node, PC)
Azar1.468 (18)10/10–10
Plantación619 (69)10/100 · 03
Sitio (Poisson)790 (51)10/100 · 034 (picos de 124)
Junto a las madres253 (54)10/100 · 0 (madres)4
  • Todas las distribuciones dan, chunk a chunk, exactamente el bosque del mundo entero, como en Mar de puntos: el halo es 2 · 7 m · 3 rondas.
  • Las densidades caen dentro del rango de las parcelas (81–1.194 por hectárea), salvo Azar, que amontona plántulas porque nada las ralea. Los adultos (edad ≥ 0,45, la de los primeros conos) son menos que los 537 de Neuquén: allí «adulto» es una talla de tronco y aquí una edad; no está calibrado.
  • En Poisson la franja más joven (0–0,1) tiene menos árboles que la siguiente: los viejos ganan los choques y pierden las plántulas que tienen cerca. La jota invertida se ve desde 0,1.
  • Generar una araucaria cuesta de 2 a 38 ms, así que la escena dibuja cada árbol desde una caché: 11 clases de edad × 3 semillas × su copa (una de las cinco, al azar) × su altura madura (de 30 a 40 m, en pasos de 2,5 m), en espejo la mitad de las veces. El pixel art no lee las escamas de las hojas, así que se generan sin ellas: un árbol en pixel art cuesta de 1 a 3 ms. Un chunk de Poisson cuesta unos 34 ms en un PC; en el móvil no está medido: la leyenda muestra el tiempo del último chunk.

Lo que no hace

Ni lenga, ni ñirre, ni coigüe: el bosque es solo de araucarias, aunque en Chile comparte las laderas con ellos (el coigüe más abajo, la lenga hasta el límite del bosque). Sin relieve, así que sin pisos de altura. Los parches son celdas de bordes rectos. Las edades no son años. Nada de esto está todavía en Islands; la propuesta es portar el núcleo a C# con la misma prueba: por chunks = entero. ✦ IA

Cómo funciona ✦ IA

Esta parte recorre el núcleo del bosque, src/scripts/araucaria-forest/core.js. Trata:

  • De la edad al sitio.
  • Edades con incendios.
  • Reutilizar las rondas de Mar de puntos, con el viejo primero.
  • Los jóvenes junto a sus madres.

1. De la edad al sitio

Una mezcla logarítmica, como el radiusAt de Mar de puntos: pasos iguales de edad multiplican el sitio por factores iguales, así que la densidad de jóvenes y viejos difiere en (7 / 1,5)², unas 22 veces.

// The room a tree of this age claims: a log blend from rYoung to rOld, reached at age 0.8.
export function radiusFor(age, p) {
  return p.rYoung * Math.pow(p.rOld / p.rYoung, smooth(0, 0.8, age));
}

2. Edades con incendios

Una exponencial da la jota invertida; el parche quemado reescribe la edad de los árboles que no lo habrían resistido.

// The background age: an exponential draw, capped at 1. Most trees are young, a few are old.
const backgroundAge = (u, p) => Math.min(1, -p.youngMean * Math.log(1 - u));

// In a burned patch a tree older than the fire lived only if it was already big then;
// one that died left room for a recruit, younger than the fire.
function naturalAge(x, y, u, w, p) {
  const a = backgroundAge(u, p), since = fireAt(x, y, p);
  if (!since || a <= since) return a;
  return a - since >= p.survive ? a : since * w;
}

3. El viejo primero

Mar de puntos exporta ahora sus rondas como resolve(), así que el bosque trae sus propios candidatos. La prioridad pone la edad en los bits altos: en cada choque gana el árbol más viejo.

// Priority: the older tree wins, ties by hash. The oldest tree was there first.
const priority = (age, hash) => ((Math.floor(age * 1023) << 22) | (hash >>> 10)) >>> 0;

// Each candidate: x, y, r, region, priority — then Sea of Points' own rounds.
c.push(x, y, radiusFor(age, p), FOREST, priority(age, hash));
const out = resolve(c, { rMax: p.rOld, rounds: 3, pair: "max", shared: true }, b);

4. Los jóvenes junto a sus madres

Las madres se calculan sobre el chunk más 50 m, lo más lejos que vuela una semilla, así que un chunk recibe también los jóvenes de madres que están en el de al lado.

// Her young: heavy seeds fall close (a Gaussian of sigma), a few fly with parakeets.
if (h01(hash, m, 0, 802) < p.flight) d = 15 + 35 * h01(hash, m, 0, 803);
else d = Math.min(3, Math.sqrt(-2 * Math.log(1 - 0.999 * h01(hash, m, 0, 804)))) * p.sigma;

SourcesFuentes

The last example of the points line of the Islands Lab: it plants the Flora Lab araucaria on the variable-r Poisson of Sea of Points. Texts marked ✦ IA were written with AI (on a heading: the whole section; ✓: reviewed by Claudio); the code, the measures and the choices were checked on this page.El último ejemplo de la línea de puntos del Islands Lab: planta la araucaria del Flora Lab sobre el Poisson de r variable de Mar de puntos. Los textos marcados con ✦ IA están escritos con IA (en un título: toda la sección; ✓: revisados por Claudio); el código, las medidas y las decisiones se comprobaron en esta página.