DATUM
← home

de methode

Hoe een wezen gebouwd wordt

Een formele beschrijving van de groeimotor: van een tabel met data tot een gerenderd organisme. Elk wezen deelt deze pijplijn; alleen de datamapping verschilt per soort.

Het systeem is een deterministisch generatief model. De invoer is een geordende reeks records r0, r1, …, rN−1, elk met een tijdstempel en een of meer gemeten velden. De groei verloopt in N discrete stappen; stap i verbruikt record ri en voegt één hoekpunt toe aan een groeiende verzameling takken. De tekening wordt volledig bepaald door de data en een vaste pseudowillekeurige hash, dus dezelfde dataset levert altijd hetzelfde organisme op. Het canvas is W×H = 720×560 met marge m = 30.

1 · kanaalextractie

Elk record wordt teruggebracht tot vier scalaire kanalen en een schokvlag. De toewijzing van bronvelden aan kanalen is het enige dat per soort verschilt; de mapping verkiest voor θ een echt gemeten richtingsveld boven elke benadering afgeleid uit de kalender.

θi = richting(ri) ∈ [0, 2π) — groeirichting ℓi = energie(ri) ∈ [0, 1] — stuurt de staplengte wi = massa(ri) ∈ [0, 1] — stuurt de dikte hi = kleur(ri) ∈ [0, 360) — tint (HSL) σi ∈ {0, 1} — schokindicator

Continue kanalen worden genormaliseerd met min–max-schaling over de dataset, met waarden begrensd tot een robuuste percentielband (meestal het 1ste–99ste) zodat één uitschieter niet kan domineren:

x̂ = clamp​( (x − xlo) / (xhi − xlo), 0, 1 )

2 · aggregatie in de tijd

Het model mikt op een leesbaar bereik van ongeveer 400 ≤ N ≤ 1300 stappen. Stel R is het aantal bruikbare rijen. De aggregatie wordt daarop afgestemd:

  • R > 1300: groeperen per dag, week of maand. Aantallen worden per groep opgeteld, niveaus gemiddeld: v̄bin = (1/|bin|) ∑ vt.
  • 400 ≤ R ≤ 1300: één rij per stap, geen aggregatie.
  • R < 400: lineaire interpolatie tussen opeenvolgende records met telkens S tussenstappen, N ≈ (R−1)·S, zodat schaarse data toch een vloeiende curve geeft. Een magere dataset mag een mager wezen laten groeien; rijkdom wordt nooit verzonnen.

3 · de groeirecursie

Groei is geen enkele wandelaar maar een verzameling actieve toppen, elk een tak met een positie p = (x, y) en richting α. Bij elke stap schuift elke top één keer op. De richting wordt bijgewerkt door een aantal hoektermen op te tellen: een duw vanuit de datarichting, een zwakke fototrope correctie, deterministische ruis en (per soort) kleine drifttermen. De precieze koppeling verschilt per soort, omdat het richtingsveld verschilt; de algemene vorm van de update is

α′ = α + δdir(ri) + ρ·Δphoto + ηi + drift

δdir is de datagestuurde hoekduw, ρ de fototrope versterking, ηi de ruis. Een concreet voorbeeld: Datum ventosa zet de windrichting via een sinus om in een duw, δdir = 0.8·sin((r − 90°)), en gebruikt ρ = 0.22; een andere soort gebruikt zijn eigen mapping.

De fototrope term voorkomt dat het organisme in zichzelf krult. Hij wordt berekend ten opzichte van een lopend zwaartepunt (cx, cy), het gemiddelde van alle hoekpunten tot nu toe, na elke stap stapsgewijs bijgewerkt. Stel φ = atan2(y − cy, x − cx) is de hoek naar buiten vanaf het zwaartepunt; de correctie Δphoto is het omgeslagen verschil tussen φ en de huidige richting, en duwt de top naar buiten:

c ← (c·n + p′) / (n+1), n ← n+1 (lopend gemiddelde) Δphoto = wrap( φ − α ) ∈ (−π, π]

De staplengte schaalt een energiekanaal naar pixels, si = s0 + ℓi·Δs, en de top schuift op met (sicosα′, sisinα′).

4 · vertakken en snoeien

De verzameling toppen is dynamisch. Een schok kan een zijtak afsplitsen onder een verschoven hoek, tot een maximum (bijvoorbeeld 14 toppen tegelijk), zodat het organisme splitst waar de data piekte. Omgekeerd worden zwakke toppen gesnoeid: een top valt weg met een kans die stijgt naarmate zijn massakanaal daalt en zijn takgeneratie dieper wordt, zolang er meer dan een paar toppen overblijven. Sterft elke top, dan wordt er een nieuwe gezaaid in het zwaartepunt.

pdeath = (1 − wi)·0.05 + g·0.012 (snoeikans)

Vertakken en snoeien halen hun beslissingen allebei uit de deterministische hash van §8, dus de topologie is reproduceerbaar.

5 · insluiting (reflectie + begrenzing)

Geen hoekpunt of controlepunt mag het kader verlaten. De standaardregel, die de meeste soorten gebruiken, spiegelt de richting wanneer een voorgestelde top een marge zou overschrijden, en berekent de positie opnieuw:

x′ ∉ [m, W−m] → α′ ← π − α′ y′ ∉ [m, H−m] → α′ ← −α′

Sommige exemplaren gebruiken een zachtere variant die de richtingsvector bij de marge van de wand wegstuurt in plaats van hem te spiegelen; het zichtbare effect is hetzelfde. In alle gevallen volgen twee harde garanties: elke top wordt begrensd tot [m, W−m]×[m, H−m], elk Bézier-controlepunt (§6) wordt op dezelfde manier begrensd zodat de curve niet kan uitpuilen, en een clip() -rechthoek op elk frame is de laatste vangrail.

6 · rendering: stuksgewijs kubische Bézier

Een tak is een polylijn van hoekpunten pk. Rechte segmenten zouden mechanisch ogen, dus elk segment pk → pk+1 wordt gerenderd als een kubische Bézier waarvan de controlepunten Catmull–Rom-raaklijnen zijn, geschat uit de buren pk−1 en pk+2 (begrensd aan de uiteinden):

c1 = pk + (pk+1 − pk−1) / 6 c2 = pk+1 − (pk+2 − pk) / 6 B(t) = (1−t)³ pk + 3(1−t)²t·c1 + 3(1−t)t²·c2 + t³ pk+1

De factor 1/6 is de uniforme Catmull–Rom-raaklijn. Let op: dit is de uniforme parametrisatie (niet chordaal of centripetaal) en de raaklijnen zijn niet genormaliseerd naar booglengte. Dat is hier aanvaardbaar, omdat de afstand tussen hoekpunten al begrensd wordt door §3.

ruwe hoekpunten kubische Bézier-keten (C1)

Links: ruwe hoekpunten met naïeve rechte segmenten. Rechts: dezelfde hoekpunten als een C¹-continue Bézier-keten, met de twee controlehandvatten van één segment zichtbaar.

7 · lijndikte, kleur en leeftijd

De lijndikte loopt taps af van basis naar top, wordt dunner met de takgeneratie g, en zwelt aan met het massakanaal; een schok voegt een litteken toe dat evenredig is met de ernst ervan ν:

frac = k / (L−1), taper = 1 − 0.7·frac w = (0.6 + 2.4·wk)·taper·(1 − 0.1·g) w ← w + 1.2 + 3.2·ν (bij schok), w ← max(w, 0.4)

Kleur wordt berekend in HSL en omgezet naar RGB. Cruciaal: er worden twee orthogonale assen gecodeerd. De tint draagt het kleurveld uit de data, de lichtheid draagt de leeftijd (oude groei donker, jonge helder), zodat tijd en kleurgegeven nooit botsen. De verzadiging ligt vast in het middenbereik; een schok verzadigt volledig en licht op:

H = hi, L = 0.30 + 0.30·(i / N), S = 0.55 schok: S = 0.95, L ↑ · leeftijd: a = i / N

De HSL→RGB-omzetting is de standaard stuksgewijze transformatie met chroma C = (1 − |2L − 1|)·S, X = C·(1 − |(H/60) mod 2 − 1|), offset m = L − C/2.

8 · deterministische ruis

Alle "willekeur" is een reproduceerbare hash van de stapindex, dus een organisme is identiek bij elke run en op elke machine. Geen PRNG-toestand, geen verschuivende seed:

rnd(s) = frac( sin(s · 12.9898) · 43758.5453 )

De klassieke sinushash. De ruisterm ηi in §3 en de jitter bij het splitsen van takken halen allebei uit deze functie, met de stap- en takindex als sleutel.

9 · schokken en het gloedveld

Een schok is het overschrijden van een drempel in een gekozen veld, meestal het 95ste percentiel van het energie- of schokkanaal, of een grens die eigen is aan het domein (een stormnorm, een crashgrens, een oversterfteratio). De ernst ervan ν ∈ [0,1] schaalt het litteken (§7) en een warme radiale gloed, getekend onder de lijn met additieve compositing, zodat de lijn scherp blijft terwijl de halo over de volgende 40 stappen uitdooft:

age = inow − ishock, recent = max(0, 1 − age/40) glow = (0.12 + 0.30·ν) · (0.35 + 0.65·recent) radius = 10 + 44·ν + 12·recent

De gloed wordt onder de takken gerenderd met globalCompositeOperation = "lighter"; de lijn wordt daarna getekend in source-over, zodat de randen scherp blijven.

10 · de dood en het printframe

Het laatste frame wordt behandeld als een print en moet zuiver blijven, dus de dood krijgt geen effect over het hele beeld. Elke taktop krijgt een verwelkte stip in de doodskleur, en de laatste stap is een ingekorte stomp in plaats van een volledige scheut: de groei werd afgebroken, niet voltooid.

rdot = max( 1.4, 1.8 + 1.8·wtip·(1 − 0.1·g) )

Voor een wezen dat uit een vaste dataset groeide, valt de sterfdatum samen met de tijdstempel van het laatste record. Omdat de maker kiest wanneer de data geëxporteerd wordt, kiest de maker het moment van de dood. Dat is de conceptuele kern, gedragen door een label onder het canvas en niet door een ingreep in het beeld.

11 · levende exemplaren

Twee wezens, het seismische en het regengevoede, groeien niet uit een vast bestand maar uit een open bron die nog groeit. Ze delen de volledige rendering en speler hierboven; alleen de datalaag verschilt, en dat verschil is bewust.

De dood ligt niet langer in de handen van de maker. Een levend wezen leeft zolang zijn bron het blijft voeden, en sterft wanneer de toevoer stilvalt. De test is een eigenschap van de data, niet van de netwerkverbinding: bereken het mediane interval tussen opeenvolgende metingen (mediaan, niet gemiddelde, om bestand te zijn tegen gaten), meet de leeftijd van de recentste meting, en verklaar het wezen pas dood wanneer die leeftijd groter is dan drie keer het mediane interval. De factor drie is een hartslagdrempel, lang genoeg om één gemiste meting te negeren, kort genoeg om een echte stop te vangen, en hij schaalt mee met de bron: een dagelijkse toevoer sterft na ongeveer drie dagen stilte, een maandelijkse na ongeveer drie maanden.

levend ⇔ (nu − tlaatst) ≤ 3 · mediaan( Δt )

De test loopt voor het eerste frame, zodat het wezen meteen in de juiste toestand opent en niet flikkert van dood naar levend. Zolang het leeft, staat er levend · laatst gevoed <datum> en groeit het wezen tot zijn volle lengte, zonder verwelkte stippen en zonder stomp; de doodsgrammatica van §10 verschijnt pas wanneer de test faalt.

Om één op zichzelf staand bestand te blijven terwijl het van een live bron afhangt, is elk levend exemplaar een hybride. Het bakt een volledige historische stam in, met een eigen parser, precies zoals een dood wezen. Bij het openen doet het één fetch() naar de open bron (USGS voor aardbevingen, Open-Meteo voor het weer, allebei met CORS en zonder sleutel), haalt elke meting sinds de grens van de stam op, en groeit door tot vandaag. Mislukt de fetch, offline of de bron verhuisde, dan valt het stil terug op de ingebakken stam.

Een eerlijke kanttekening die het werk zonder omwegen maakt: de ingebakken stam van beide levende exemplaren is op dit moment synthetisch, structureel trouw maar verzonnen, omdat de bouwomgeving geen open internet had om de echte geschiedenis op te halen. De live laag corrigeert dat zodra het bestand online geopend wordt, en groeit vanaf de grens van de stam verder op echte data; maar de geschiedenis van voor die grens is aannemelijk, niet gemeten.

lees het volledige statement →

DATUM · Jurgen De Wever
jrgndwvr.be