(Acest articol a fost publicat pentru prima dată pe CHI(χ)-Fișiereși cu amabilitate a contribuit la R-bloggeri). (Puteți raporta problema legată de conținutul acestei pagini aici)
Doriți să vă distribuiți conținutul pe R-bloggeri? dați clic aici dacă aveți un blog, sau aici dacă nu aveți.
Chiar am vrut doar să vărs niște cerneală pentru stilou. Nu la propriu. Asta ar fi scump. 🖋️
Am vrut să iau culori inspirate de Pilot’s Iroshizuku cerneluri stilou, aruncați-le undeva pe o pânză goală și lăsați-le să curgă.
Asta m-a condus la câmpuri de curgere.
În primul rând: unde arunc cerneala?
Înainte de a mă îngrijora cum se mișcă ceva, am nevoie de câteva puncte de plecare.
Pentru asta, m-am întors la un vechi favorit: filotaxie – aranjamentul matematic asociat cu semințele de floarea soarelui și alte structuri ale plantelor.

Partea importantă este unghi de aur – aproximativ 137,5^.
Fiecare punct nou se rotește cu acel unghi și se deplasează puțin mai departe de centru.

Acest lucru îmi oferă un set determinist de locații.
Nici un flux încă. Doar locuri unde pot arunca particule.
Ce este un câmp de flux?
Aceasta a fost partea pe care am făcut-o inițial mult mai complicată în mintea mea decât trebuia. Un câmp de flux este pur și simplu o regulă:
La poziție
(x, y)in ce directie ar trebui sa merg?
De exemplu, acest câmp:
field_vortex <- function(x, y) {
atan2(y, x) + pi / 2
}
returnează o direcție perpendiculară pe dreapta de la origine.
Îmi place să mă gândesc la domeniu ca la un peisaj invizibil.
Peisajul nu desenează nimic în sine.
Trebuie să arunc ceva în el.
💧
Plimbătorul
O particulă începe la (x0, y0).
La fiecare pas:
- întreabă câmpul în ce direcție ar trebui să meargă,
- transformă acel unghi în mișcare orizontală și verticală,
- face un pas mic,
- întreabă din nou.
Partea importantă a mersului este în esență:
angle <- field(x, y) x_new <- x + cos(angle) * step_size y_new <- y + sin(angle) * step_size
cos(angle) dă componenta orizontală a mișcării. sin(angle) dă componenta verticală.
Şi step_size determină cât de departe se mișcă particula de fiecare dată. Deci o curbă nu este de fapt trasată. Înregistrez istoricul unei particule în mișcare. Această distincție a făcut, în sfârșit, câmpuri de flux să dea clic pentru mine.
Aruncați cerneala Iroshizuku și lăsați-o să curgă
Acum pot combina cele două idei. Filotaxia decide unde se nasc particulele. Câmpul de flux decide ce se întâmplă cu ei după aceea.
Spirală
Trage potecile în jurul centrului în timp ce se deplasează treptat spre exterior – un pic ca cerneala care se învârte în afară în timp ce amesteci o ceașcă
Cod
spiral_paths <- make_flow_paths( field = field_spiral, # rule that determines which direction each particle moves n = 500, # number of particles dropped into the field n_steps = 220, # how many steps each particle takes = like how LONG it keeps walking step_size = 0.006 # distance travelled with each step = how FAR it moves each time ) plot_flow_paths( spiral_paths, linewidth = 0.9, # thickness of each particle trail alpha = 0.6 # transparency: lower = more see-through )


Același model de naștere. Diferite legi ale fizicii.
Schimbați câmpul, schimbați lumea
Iată partea care mi se pare dependență.
Nu trebuie să rescriu walker-ul.
Pot doar să-i dau o funcție diferită.
Geometria de pornire rămâne aceeași.
Procesul de mers pe jos rămâne același.
Numai câmpul se schimbă – și odată cu el, întreaga lume prin care se deplasează particulele.
Câmpuri radiale
Aceste câmpuri sunt toate centrate în jurul originii, dar ghidează particulele în moduri diferite: rotind, în spirală, deplasându-se în exterior sau trăgând spre interior.
Trage căile în mișcare circulară – un pic ca traseele de stele într-o fotografie cu expunere lungă.
Cod
draw_flow_field( field = field_vortex, # circular motion around the centre n = 500, # lower = sparser star trails; higher = denser sky n_steps = 220, # lower = shorter trails, like ending the exposure early step_size = 0.006, # lower = smoother, tighter curves; higher = bigger jumps linewidth = 1.2, # lower = finer light trails; higher = bolder streaks alpha = 0.6 # lower = softer/fainter trails; higher = more opaque )


Trage potecile în jurul centrului în timp ce se deplasează treptat spre exterior – un pic ca cerneala care se învârte în exterior în timp ce amesteci o ceașcă.
Cod
draw_flow_field( field = field_spiral, # circular motion with a gentle outward drift n = 500, # lower = fewer ink trails; higher = denser layering n_steps = 220, # lower = shorter spirals; higher = longer outward journeys step_size = 0.006, # lower = smoother spirals; higher = bigger jumps through the field linewidth = 0.9, # lower = finer strands; higher = thicker ink strokes alpha = 0.6 # lower = softer layering; higher = stronger overlap )


Împinge căile departe de centru, cum ar fi scânteile sau cerneala aruncate în afară dintr-un singur punct.
Cod
draw_flow_field( field = field_outward, # motion directly away from the centre n = 500, # lower = fewer outward traces; higher = denser burst n_steps = 120, # lower = shorter bursts; higher = longer radiating paths step_size = 0.01, # lower = smoother expansion; higher = more dramatic outward jumps linewidth = 0.8, # lower = finer streaks; higher = bolder marks alpha = 0.6 # lower = softer trails; higher = stronger opacity )


Atrage căile înapoi spre centru, ca și cum totul de pe pagină ar fi tras ușor spre interior.
Cod
draw_flow_field( field = field_inward, # motion directly toward the centre n = 500, # lower = fewer inward traces; higher = denser convergence n_steps = 120, # lower = shorter inward motion; higher = longer collapsing paths step_size = 0.01, # lower = smoother inward pull; higher = more abrupt movement linewidth = 0.8, # lower = finer threads; higher = heavier strokes alpha = 0.6 # lower = softer layering; higher = stronger overlap near the centre )


Câmpuri cu valuri
Aceste câmpuri sunt mai puțin despre centru și mai mult despre mișcarea de-a lungul paginii: balansare, înclinare, ciocnire și ondulare.
Îndoaie cărările înainte și înapoi, ca firele libere de alge marine care plutesc odată cu curentul.
Cod
draw_flow_field( field = field_wavy, # back-and-forth motion, like seaweed drifting in a current n = 500, # lower = fewer strands; higher = a fuller, more tangled flow n_steps = 150, # lower = shorter strands; higher = longer, more continuous sweeps step_size = 0.01, # lower = smoother bends; higher = looser, more exaggerated movement linewidth = 2, # lower = finer strands; higher = thicker, more painterly strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlapping colour )


Deplasează căile de-a lungul unui curent înclinat, ca și cum o briză le-ar împinge în diagonală pe pagină.
Cod
draw_flow_field( field = field_diagonal, # slanted wave-like motion across the page n = 500, # lower = fewer paths; higher = denser layering n_steps = 125, # lower = shorter marks; higher = longer, more continuous paths step_size = 0.01, # lower = smoother motion; higher = larger jumps linewidth = 1, # lower = finer strands; higher = thicker strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlap )


Lasă modelele de valuri care se încrucișează să se împingă unul împotriva celuilalt, creând o mișcare mai încurcată, țesătură.
Cod
draw_flow_field( field = field_interference, # crossing wave patterns, like ripples meeting on water n = 500, # lower = more open crossings; higher = denser woven structure n_steps = 250, # lower = shorter fragments; higher = longer interlaced paths step_size = 0.005, # lower = finer, smoother weaving; higher = more abrupt directional changes linewidth = 0.5, # lower = delicate threads; higher = heavier woven strokes alpha = 0.6 # lower = softer overlaps; higher = stronger visual interference )


Împinge căile prin inelele în expansiune, un pic ca niște ondulații care se răspândesc în exterior pe apă.
Cod
draw_flow_field( field = field_ripple, # radial wave motion, like ripples spreading across water n = 500, # lower = fewer visible ripple traces; higher = denser layering n_steps = 180, # lower = shorter paths; higher = longer ripple-following trails step_size = 0.008, # lower = smoother ripples; higher = more exaggerated movement linewidth = 0.8, # lower = finer rings; higher = bolder ripple marks alpha = 0.6 # lower = softer layering; higher = stronger overlap )


Particulele în sine nu s-au schimbat.
Plimbătorul nu s-a schimbat.
Doar domeniul s-a schimbat – și asta este suficient pentru ca fiecare lume să se simtă diferită.
Cum n schimbă mulțimea
Înainte de a schimba cât de departe parcurg particulele, pot schimba ceva și mai simplu: câte particule eliberez în câmp.
n controlează numărul de puncte de plecare.
Câmpul nu se schimbă.
Plimbătorul nu se schimbă.
Fiecare particulă urmează aceleași reguli.
Pur și simplu sunt mai mulți – sau mai puțini – călători care se deplasează prin aceeași lume.
Cu un mic nstructura câmpului se simte rară și expusă.
Ca n crește, căile individuale încep să se suprapună și desenul devine mai dens, mai bogat și mai mult ca o textură continuă.
Cod
draw_flow_field( field = field_vortex, # same circular field in every tab n = 50, # very few particles = sparse, isolated trails n_steps = 180, # fixed so journey length does not change step_size = 0.006, # fixed so stride does not change linewidth = 1.2, # fixed drawing style alpha = 0.6 # fixed transparency )


Cod
draw_flow_field( field = field_vortex, # same circular field in every tab n = 100, # more particles = more of the field becomes visible n_steps = 180, # fixed so journey length does not change step_size = 0.006, # fixed so stride does not change linewidth = 1.2, # fixed drawing style alpha = 0.6 # fixed transparency )


Cod
draw_flow_field( field = field_vortex, # same circular field in every tab n = 250, # overlapping trails begin to create a fuller structure n_steps = 180, # fixed so journey length does not change step_size = 0.006, # fixed so stride does not change linewidth = 1.2, # fixed drawing style alpha = 0.6 # fixed transparency )


Cod
draw_flow_field( field = field_vortex, # same circular field in every tab n = 500, # many particles = dense, layered star trails n_steps = 180, # fixed so journey length does not change step_size = 0.006, # fixed so stride does not change linewidth = 1.2, # fixed drawing style alpha = 0.6 # fixed transparency )


Schimbarea n nu modifică regulile de mișcare. Se schimbă cât de dens sunt eșantionate aceste reguli.
Un câmp rară îmi oferă căi individuale.
Un câmp aglomerat începe să dezvăluie o textură.
Cum n_steps schimbă călătoria
Păstrând același câmp și începând geometria, am variat doar n_steps pentru a vedea cum lungimea călătoriei schimbă desenul final.
Cod
draw_flow_field( field = field_diagonal, # slanted wave-like motion across the page n = 500, # lower = fewer paths; higher = denser layering n_steps = 5, # very short paths step_size = 0.01, # lower = smoother motion; higher = larger jumps linewidth = 2, # lower = finer strands; higher = thicker strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_diagonal, # slanted wave-like motion across the page n = 500, # lower = fewer paths; higher = denser layering n_steps = 25, # medium-length paths step_size = 0.01, # lower = smoother motion; higher = larger jumps linewidth = 2, # lower = finer strands; higher = thicker strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_diagonal, # slanted wave-like motion across the page n = 500, # lower = fewer paths; higher = denser layering n_steps = 125, # long flowing paths step_size = 0.01, # lower = smoother motion; higher = larger jumps linewidth = 2, # lower = finer strands; higher = thicker strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_diagonal, # slanted wave-like motion across the page n = 500, # lower = fewer paths; higher = denser layering n_steps = 250, # long flowing paths step_size = 0.01, # lower = smoother motion; higher = larger jumps linewidth = 2, # lower = finer strands; higher = thicker strokes alpha = 0.7 # lower = lighter layering; higher = stronger overlap )


Cum step_size schimbă pasul
Dacă n_steps controlează cât durează călătoria, step_size controlează pasul particulei.
Aici am păstrat câmpul, numărul de particule, numărul de pași și stilul de desen la fel și am schimbat doar step_size. Valorile mai mici urmăresc câmpul mai delicat, în timp ce valorile mai mari se deplasează mai departe la fiecare pas și exagerează mișcarea.
Cod
draw_flow_field( field = field_spiral, # circular motion with a gentle outward drift n = 500, # lower = fewer ink trails; higher = denser layering n_steps = 50, # fixed here so only step_size changes step_size = 0.001, # tiny stride = very fine, tightly sampled motion linewidth = 0.8, # lower = finer strands; higher = thicker ink strokes alpha = 0.7 # lower = softer layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_spiral, # circular motion with a gentle outward drift n = 500, # lower = fewer ink trails; higher = denser layering n_steps = 50, # fixed here so only step_size changes step_size = 0.01, # medium stride = balanced between smoothness and movement linewidth = 0.8, # lower = finer strands; higher = thicker ink strokes alpha = 0.7 # lower = softer layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_spiral, # circular motion with a gentle outward drift n = 500, # lower = fewer ink trails; higher = denser layering n_steps = 50, # fixed here so only step_size changes step_size = 0.08, # large stride = exaggerated jumps through the field linewidth = 0.8, # lower = finer strands; higher = thicker ink strokes alpha = 0.7 # lower = softer layering; higher = stronger overlap )


Cod
draw_flow_field( field = field_spiral, # circular motion with a gentle outward drift n = 500, # lower = fewer ink trails; higher = denser layering n_steps = 50, # fixed here so only step_size changes step_size = 4, # large stride = exaggerated jumps through the field linewidth = 0.8, # lower = finer strands; higher = thicker ink strokes alpha = 0.7 # lower = softer layering; higher = stronger overlap )


Cu aceeași lungime de călătorie, schimbarea doar a pasului face ca potecile să se simtă foarte diferite. Pașii mici îmbrățișează câmpul mai strâns, în timp ce pașii mai mari produc o mișcare mai îndrăzneață și mai liberă.
Un sistem minuscul
Ce a început ca „Vreau să vărs niște cerneală” s-a transformat într-o mică lecție de programare surprinzător de utilă.
Sistemul are acum trei idei independente:
STARTING POINTS
↓
particles
↓
FLOW FIELD
↓
direction
↓
WALKER
↓
paths
Geometria de pornire răspunde:
De unde începi?
Câmpul răspunde:
Având în vedere unde vă aflați, în ce direcție ar trebui să mergeți?
Iar plimbătorul răspunde:
Cum te deplasezi prin acel câmp – cât de lungă este călătoria și cât de mare este fiecare pas?
Odată ce aceste responsabilități sunt separate, o pot schimba pe una fără a le rescrie pe celelalte.
Ceea ce înseamnă că acum sunt mult prea multe lucruri pe care vreau să le încerc. 😬
Geometrii de pornire diferite. Diferite domenii matematice. Zgomot. Răsuci.
Poate chiar și particule care acordă atenție reciprocă în loc să asculte doar mediul înconjurător.
Dar asta e pentru o altă scurgere de cerneală.
