Iroshizuku întâlnește un câmp de curgere

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

(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:

  1. întreabă câmpul în ce direcție ar trebui să meargă,
  2. transformă acel unghi în mișcare orizontală și verticală,
  3. face un pas mic,
  4. î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
)

Sute de căi colorate care încep într-un aranjament filotactic și se curbe printr-un câmp de curgere în spirală.Sute de căi colorate care încep într-un aranjament filotactic și se curbe printr-un câmp de curgere în spirală.

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ă.

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.