memoscan
← all memos

Memo 0xf5ae81b0…09b29c on Ethereum

opening/0.40, smooth(0.50,0.95,fgrow)*0.8), 0, 1); // green when young → colours up as it nears bloom drawBud(top[0], top[1], R*0.56, 0.14*sgn, ripe, fgrow, hRs); } } } // ===================== COLONY: 1 → many plants from the seed ===================== // Lotuses grow in STANDS — default to a pond BUNCH (solo is now the minority). Companions SCATTER // across the FULL width (stratified slots L→R, so neither side is empty), near (big, low, opaque) → // far (small, high, faint) for depth. Big bunches put many plants + many blooms across the canvas. const cr=rng('colony',st.seed), roll=cr(); const nC = roll<0.14 ? 0 : roll<0.36 ? 1+Math.floor(cr()*2) // 1–2 : roll<0.68 ? 2+Math.floor(cr()*3) // 2–4 : 4+Math.floor(cr()*6); // 4–9 (a full pond bunch) const comps=[]; for(let k=0;k<nC;k++){ const r=rng('comp',st.seed,k); const frac=nC>1? k/(nC-1) : 0.5; // stratified L edge → R edge so BOTH sides fill const slot=clamp(frac + (r()*2-1)*(0.7/nC), 0, 1); // + jitter (still spans the full width) const cxP=clamp(FRAME+45 + slot*(W-2*FRAME-90), FRAME+40, W-FRAME-40); const depth=clamp(0.10+0.85*r(),0,1); const S=lerp(0.80,0.34,depth), fade=lerp(0.20,0.64,depth); const byP=waterY - depth*46; // far plants sit higher (toward the horizon) const mm=clamp(m + (r()*2-1)*0.32, 0.18, 1), vv=clamp(v*(0.6+0.55*r()), 0.2, 1), ssn=clamp(sen + (r()*2-1)*0.12, 0, 1); comps.push({cx:cxP, by:byP, S, fade, m:mm, v:vv, sen:ssn, density:clamp(mm*1.05+(r()*2-1)*0.15, 0.15, 1), rewards:clamp(rewards*(0.55+0.7*r()), 0, 1), reproOK:mm>=0.55, browny:clamp(Math.max(1-vv,ssn),0,1), dead:false, pseed:'c'+k, depth}); } comps.sort((a,b)=>b.depth-a.depth).forEach(paintLotus); // far (faint) → near paintLotus({cx:baseX, by:waterY, S:nC>4?0.95:nC>0?1.05:1.14, fade:0, m, v, sen, density, rewards, reproOK, browny, dead, pseed:'hero'}); }, traits(st,env){ const cr=rng('colony',st.seed), roll=cr(); const nC = roll<0.14 ? 0 : roll<0.36 ? 1+Math.floor(cr()*2) : roll<0.68 ? 2+Math.floor(cr()*3) : 4+Math.floor(cr()*6); const total = 1 + nC; const vx=rng('variety',st.seed,'hero')(), variety = vx<0.42?'Pink':vx<0.80?'White':'Rose'; const florif = 0.7 + 0.6*rng('florif',st.seed,'hero')(); const nFlow = env.reproOK ? clamp(Math.round((1 + env.rewards*3.4)*florif), 1, 5) : 0; const wS=Math.pow(rng('windS',st.seed)(),2.6), wArrow=rng('windD',st.seed)()<0.5?'←':'→'; const wind = wS<0.12?'Still' : (wS<0.4?'Breezy':wS<0.7?'Windswept':'Storm')+' '+wArrow; return [ ['Pads', clamp(Math.ceil(1+env.density*4),1,5)], ['Flowers', st.diedAt>0?'—':nFlow], ['Bloom', st.diedAt>0?'Dry showerhead' : (env.m>=0.55 && env.rewards>0.3 ? (env.season==='Summer'?'Open':'In bud') : '—')], ['Variety', variety], ['Colony', total===1?'Solo':total===2?'Pair':total<=4?'Cluster':'Bunch'], ['Wind', wind], ]; } })); })(); ; /* species/maple.js */ /* maple — species index 9. GENERATED from prototypes/japanese-maple.html by extract.js. Do not edit here — edit the prototype and re-run `npm run extract`. The prototype's mount() call is redirected to SeedlingRenderer.registerSpecies via the local shim. */ (function () { const mount = (spec) => SeedlingRenderer.registerSpecies(9, spec); /* Japanese Maple — Acer palmatum. A spreading, layered specimen tree whose TRADEMARK is the deeply-lobed PALMATE STAR leaf (5 or 7 pointed lobes from one point, middle lobe longest), drawn as a solid '*'-filled silhouette so it reads. DRAMATIC day-of-year colour arc: bare winter twigs+buds → red-bronze spring flush → green (or burgundy cultivar) summer → a MOTTLED blaze of gold/orange/scarlet autumn (each leaf turns at its own pace) → brown & fall. Winged SAMARAS (keys) + tiny red spring florets. Leaves/samaras/flowers stay LITERAL (scheme-exempt). Ported to the shared core. */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='maple'); function mapleDNA(seed){ const dd=rng('dna',seed), g=()=>(dd()+dd()+dd()-1.5)/1.5; return { dominance:0.18+0.32*dd(), divergence:0.6+0.5*dd(), droop:0.35+0.5*dd(), gnarl:0.3+0.5*dd(), taperLen:0.66+0.12*dd(), lateralN:1.0+0.8*dd(), reach:0.9+0.5*dd(), lean:g()*0.12, stature:0.84+0.2*dd(), maxOrder:4+(dd()<0.5?1:0), leafiness:0.8+0.4*dd(), lobes:dd()<0.5?5:7, cultivar:Math.pow(dd(),3.2)*0.9, autumnHue:dd(), greenHold:0.25+0.5*dd(), samaraAb:0.4+0.6*dd(), windDir:dd()<0.5?1:-1, windStr:Math.pow(dd(),2.5) }; } mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Japanese Maple', sub:'acer v2', senFrom:0.85, schemeExempt:['flowers','leaves'], // palmate stars + samaras carry literal seasonal colour LW:{season:1.0, stem:1.15, leaves:1.05, flowers:1.4}, LS:{season:1.0, stem:1.0, leaves:1.05, flowers:0.85}, note:'Spreading layered tree · palmate star leaves · deciduous colour arc (bare → spring red → green → autumn blaze) · winged samaras · mostly solitary specimen.', PAL:{ soil:'#6f5128', dry:'#7a5a30', grey:'#9a8f74', bark:'#7A5E48', barkDk:'#523E2C', green:'#4E8A38', greenLt:'#79B24E', burgundy:'#9A3A4E', springRed:'#D85A44', gold:'#F2C233', orange:'#F0801E', scarlet:'#E22820', crimson:'#B81830', brown:'#9A6A34', flower:'#B81E32', samara:'#C88A6A', samaraDry:'#9A7E4A', bud:'#8A4A3C' }, stageName(st,env){ if(st.diedAt>0)return'Sealed'; const m=env.m, doy=(((env.now+70)%365)+365)%365; if(env.ageF>0.86)return'Veteran'; if(m<0.12)return'Sapling'; if(m<0.4)return'Young'; if(m<0.7)return'Branching'; if(doy<60||doy>344)return'Dormant'; if(doy>248)return'Ablaze'; return'Crown'; }, draw(st, C, env){ const {m,v,sen,season,density,rewards,ageF,baseX,baseY,now}=env, P=this.PAL; const bgC = BACKDROPS[backdropFor(st.seed)]; // ---- the DRAMA: day-of-year drives a deciduous colour arc (shared across all plants) ---- // GERM offset: a maple germinates in SPRING, so age 0 maps to leaf-out (a new seedling is leafy, // not a bare winter twig). The bare-winter dormancy then falls on older, established trees. const GERM=70, doy=(((now+GERM)%365)+365)%365; const leafOut=clamp(smooth(56,84,doy)*(1-smooth(330,358,doy)),0,1); // bare winter → flush spring → fall late autumn const springFlush=smooth(58,82,doy)*(1-smooth(96,134,doy)); // new red-bronze growth const fire=smooth(248,300,doy); // autumn colour ramps up const fadeAut=smooth(322,352,doy); // late autumn: browning before fall const foliageF=clamp(leafOut*(1-sen*0.85)*(0.45+0.55*v),0,1); // canopy payoff ← care (vitality), not reproduction const bareWinter = leafOut<0.25; function paintMaple(I){ const {seed, artSeed, cx, by, S, dim, m, density, v, sen, rewards} = I; const FC = c => dim>0 ? mix(c, bgC, dim) : c; const D=mapleDNA(seed); const bark=FC(mix(P.bark,P.grey,Math.max((1-v)*0.3,sen*0.5))), barkDk=FC(mix(P.barkDk,P.grey,Math.max((1-v)*0.3,sen*0.5))); // per-leaf seasonal colour — mottled so leaves turn at their own pace function leafColor(rs){ let c=mix(P.green, P.burgundy, D.cultivar); c=mix(c, P.springRed, springFlush*(0.45+0.4*rs())); const turn=clamp(fire*1.75 - rs()*0.45 - D.greenHold*0.28, 0, 1); const au=mix(P.gold, mix(P.orange,P.scarlet,D.autumnHue), 0.2+0.6*rs()); c=mix(c, au, turn); c=mix(c, P.brown, fadeAut*0.55*(0.6+0.4*rs())); c=mix(c, P.dry, sen*0.4); return FC(shade(c,rs)); } // THE TRADEMARK: deeply-lobed PALMATE STAR leaf (solid '*' fill so the silhouette reads) function mapleLeaf(cx0,cy0,baseAng,size,rs){ if(foliageF<=0.04) return; const col=leafColor(rs), edgeCol=FC(shade(mix(col,'#000000',0.16),rs)); const nL=D.lobes, notch=0.5, spreadMax=2.6; for(let gx=-size;gx<=size;gx+=GRID) for(let gy=-size;gy<=size;gy+=GRID){ const r=Math.hypot(gx,gy); let phi=Math.atan2(gy,gx)-baseAng; phi=Math.atan2(Math.sin(phi),Math.cos(phi)); if(Math.abs(phi)>spreadMax && r>GRID*1.2) continue; const frac=clamp((phi+spreadMax)/(2*spreadMax),0,1); const lobeWave=Math.pow(Math.abs(Math.cos(frac*(nL-1)*Math.PI)),2.0); const sideTaper=0.62+0.38*Math.cos(phi); const boundary=Math.max(GRID*0.8, size*sideTaper*(notch+(1-notch)*lobeWave)); if(r<=boundary){ const edge=r>boundary-GRID*1.0; C.push(cx0+gx,cy0+gy, 56, '*', edge?edgeCol:col, 'leaves'); } } } function leafSpray(x,y,twigDir,size,rs){ const px=-Math.sin(twigDir), py=Math.cos(twigDir), sd=rs()<0.5?1:-1; let pa=Math.atan2(py*sd,px*sd); pa += angTo(pa,UP)*0.4; const ex=x+Math.cos(pa)*GRID*2.4*S, ey=y+Math.sin(pa)*GRID*2.4*S; C.push((x+ex)/2,(y+ey)/2, 55, dirSym(Math.cos(pa),Math.sin(pa)), FC(shade(P.springRed,rs)), 'leaves'); mapleLeaf(ex,ey, pa, size*(0.85+0.3*rs()), rs); } function samara(x,y,rs){ const col=FC(mix(P.samara, P.samaraDry, Math.max(fadeAut,fire*0.5,sen))); for(const sd of [1,-1]){ let dir=DOWN + sd*(0.55+0.2*rs()), pos=[x,y]; const len=3+Math.floor(rs()*2); for(let i=1;i<=len;i++){ dir+=angTo(dir,DOWN)*0.12; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1],84,dirSym(Math.cos(dir),Math.sin(dir)),shade(col,rs),'flowers'); } C.push(x+sd*GRID*0.4, y+GRID*0.6, 85, '.', FC(shade(mix(P.brown,col,0.4),rs)), 'flowers'); } } function flowerCluster(x,y,rs){ const n=3+Math.floor(rs()*3); for(let k=0;k<n;k++){ const a=rs()*TAU, r=GRID*(1+rs()); C.push(x+Math.cos(a)*r, y+Math.sin(a)*r+GRID*0.5, 88, '.', FC(shade(P.flower,rs)), 'flowers'); } C.push(x,y,89,'.',FC(shade(mix(P.flower,'#000000',0.2),rs)),'flowers'); } function bud(x,y,rs){ C.push(x,y,80,'O',FC(shade(P.bud,rs)),'flowers'); C.push(x,y-GRID*0.6,81,'.',FC(shade(mix(P.bud,P.springRed,springFlush),rs)),'flowers'); } function sprout(rs,rx){ if(1-smooth(0.05,0.20,m) <= 0.02) return; const g=smooth(0,0.18,m), hLen=(52+46*g)*S; let dir=UP+D.lean*1.2, pos=[rx,by-3]; const steps=Math.max(3,Math.ceil(hLen/GRID)); for(let i=1;i<=steps;i++){ const u=i/steps; dir+=Math.sin(u*2.6+D.lean*4)*0.045; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1],50,dirSym(Math.cos(dir),Math.sin(dir)),shade(bark,rs),'stem'); } if(bareWinter){ bud(pos[0],pos[1],rs); return; } // juvenile seedling carries a small SPRAY of first palmate leaves (not a bare whip with one leaf) const nl=1+Math.round(clamp(g,0,1)*2.4); // 1–3 first leaves for(let k=0;k<nl;k++){ const a=UP + (k-(nl-1)/2)*0.52 + (rs()*2-1)*0.16; leafSpray(pos[0],pos[1], a, (6.5+6*g)*S, rs); } if(g>0.5){ const md=steps>3?Math.floor(steps*0.6):2, mp=[rx+Math.cos(UP)*GRID*md, by-3+Math.sin(UP)*GRID*md]; // a lower pair along the stem for(const sd of [1,-1]) if(rs()<0.7) leafSpray(mp[0],mp[1], UP+sd*0.7, (5+5*g)*S, rs); } } const growWin=0.12, maxOrder=D.maxOrder; let budget=8000; const chain=(1-Math.pow(D.taperLen,maxOrder+1))/(1-D.taperLen), baseRun=((by-300)/chain)*D.stature*(0.9+0.1*v)*S; function grow(x,y,head,vigor,order,birthM,pid,sideSeq){ if(budget<=0) return; const grown=clamp((m-birthM)/growWin,0,1); if(grown<=0) return; budget--; const rs=rng('mb', seed, pid); let run=baseRun*Math.pow(D.taperLen,order)*(0.82+0.36*rs())*grown; if(order===0) run*=0.82; const steps=Math.max(1,Math.ceil(run/GRID)), woodM=smooth(0.10,0.85,m), baseThick=(maxOrder-order)/maxOrder, bz=54-order; const f1=0.5+1.4*rs(), f2=1.1+2.2*rs(), ph1=rs()*6.28, ph2=rs()*6.28, gnarlA=D.gnarl*0.045*(1+order*0.12), bears=order>=2 ? D.leafiness : 0; const canChild = grown>=1 && order<maxOrder; const nLat = order===0 ? 2+Math.floor(rs()*3) : clamp(Math.round(D.lateralN*(1-0.35*D.dominance)+gauss(rs)*0.9),0, order<2?3:2); const lo=order===0?0.22:0.30, hi=0.93, pts=[], sides=[]; let lastSide=rs()<0.5?1:-1; for(let c=0;c<nLat;c++){ pts.push(clamp(lerp(lo,hi,(c+0.5)/Math.max(nLat,1)) + (rs()*2-1)*(0.6/Math.max(nLat,1)), 0.1, 0.95)); if(order===0 || rs()<0.72) lastSide=-lastSide; sides.push(lastSide); } pts.sort((a,b)=>a-b); let dir=head, pos=[x,y], ci=0; for(let i=1;i<=steps;i++){ const u=i/steps; const photo=lerp(0.12,0.035,clamp(order/3,0,1))*Math.max(0,1-order/3.2), grav=0.12*D.droop*smooth(1.4,maxOrder,order)*(0.25+0.75*u); dir += angTo(dir,UP)*photo + angTo(dir,DOWN)*grav + gnarlA*(Math.sin(i*0.5*f1+ph1)+0.5*Math.sin(i*0.3*f2+ph2)); const exposure=clamp((by-pos[1])/600,0,1); dir += D.windDir*D.windStr*0.06*exposure*(0.35+0.65*order/maxOrder); const dx=Math.cos(dir), dy=Math.sin(dir); pos=[pos[0]+dx*GRID, pos[1]+dy*GRID]; const sym=dirSym(dx,dy); C.push(pos[0],pos[1], bz, sym, shade(bark,rs), 'stem'); const px=-dy, py=dx; const thick=clamp(Math.round(baseThick*baseThick*1.9*woodM*(0.7+0.3*v)*(1-0.5*u)*smooth(0.12,0.85,grown)),0,2); for(let wq=1;wq<=thick;wq++){ C.push(pos[0]+px*wq*GRID, pos[1]+py*wq*GRID, bz, sym, shade(barkDk,rs), 'stem'); C.push(pos[0]-px*wq*GRID, pos[1]-py*wq*GRID, bz, sym, shade(barkDk,rs), 'stem'); } if(order>=2 && u>0.1 && i%3===1){ if(bareWinter){ if(rs()<0.22) bud(pos[0],pos[1],rs); } else if(rs()<bears){ const side=((sideSeq+(i/2|0))%2===0)?1:-1; leafSpray(pos[0],pos[1], Math.atan2(py*side,px*side), (12+11*m)*(0.85+0.3*rs())*S, rs); } if(springFlush>0.3 && rewards>0.25 && rs()<0.05) flowerCluster(pos[0],pos[1],rs); if((fadeAut>0.2 || (springFlush<0.2 && leafOut>0.6 && doy>150)) && rewards>0.25 && rs()<0.05*D.samaraAb) samara(pos[0],pos[1],rs); } while(canChild && ci<pts.length && u>=pts[ci] && budget>0){ const side=sides[ci], div=(order===0?(0.55+0.55*D.reach):D.divergence*(0.7+0.7*rs())*D.reach); let la=dir + side*div + gauss(rs)*0.14; la += angTo(la,UP)*(order===0?0.05:0.08)*(1-D.reach*0.3); const cv=vigor*(order===0?(0.72+0.26*rs()):(0.45+0.4*(1-D.dominance))*(0.8+0.3*rs())); grow(pos[0],pos[1], la, cv, order+1, birthM+growWin, pid*7+ci+2, sideSeq+ci+1); ci++; } } if(canChild && budget>0){ const leadV=vigor*lerp(0.38,0.9,D.dominance); grow(pos[0],pos[1], dir+angTo(dir,UP)*(order<=1?0.26:0.12)+gauss(rs)*0.06, leadV, order+1, birthM+growWin, pid*7+1, sideSeq+1); } else if(grown>=1){ if(bareWinter){ bud(pos[0],pos[1],rs); } else { leafSpray(pos[0],pos[1], dir, (15+13*m)*S, rs); if(rs()<0.5) leafSpray(pos[0],pos[1], dir+0.7, (13+11*m)*S, rs); } } else if(order>=2 && !bareWinter){ leafSpray(pos[0],pos[1], dir, (13+10*m)*S, rs); } } const rootX=cx - D.windDir*D.windStr*48*S; sprout(rng('sprout',seed), rootX); grow(rootX, by-3, UP+D.lean*0.6+D.windDir*D.windStr*0.07, 1, 0, 0, 1, 0); } // ===================== SETTING: maples are specimen trees (mostly Solo) ===================== const roll=rng('setting',st.seed)(); const nC = roll<0.5?0 : roll<0.82?1 : 2+Math.floor(rng('fn',st.seed)()*2); const comps=[]; for(let i=0;i<nC;i++){ const cseed='0x'+(hashStr(st.seed+'|c'+i)>>>0).toString(16), rc=rng('comp',st.seed,i), side=(i%2===0?1:-1), f=i/Math.max(1,nC); comps.push({seed:cseed, artSeed:cseed, cx:baseX+side*(170+i*70+rc()*40), by:baseY-(4+i*10+rc()*16), S:lerp(0.58,0.42,f)*(0.9+0.2*rc()), dim:lerp(0.42,0.60,f), m:clamp(m+(rc()*2-1)*0.06,0.04,1), density, v, sen, rewards, depth:f}); } comps.sort((a,b)=>b.depth-a.depth).forEach(paintMaple); paintMaple({seed:st.seed, artSeed:st.artSeed, cx:baseX, by:baseY, S:1.0, dim:0, m, density, v, sen, rewards}); }, traits(st,env){ const D=mapleDNA(st.seed); const roll=rng('setting',st.seed)(); const setting = roll<0.5?'Solo' : roll<0.82?'Pair' : 'Grove'; const doy=(((env.now+70)%365)+365)%365; const colour = (doy<56||doy>352)?'Bare' : doy<96?'Spring red' : doy<240?'Green' : doy<300?'Turning' : 'Autumn fire'; const cult = D.cultivar>0.55?'Purple' : D.cultivar>0.25?'Bronze' : 'Green'; const auh = D.autumnHue>0.6?'Scarlet' : D.autumnHue>0.3?'Orange' : 'Gold'; return [ ['Setting', setting], ['Colour now', colour], ['Lobes', D.lobes], ['Cultivar', cult], ['Autumn', auh], ['Wind', D.windStr<0.12?'Still':D.windStr<0.40?'Breezy':D.windStr<0.70?'Windswept':'Storm-bent'], ]; } })); })(); ; /* species/poppy.js */ /* poppy — species index 1. GENERATED from prototypes/field-poppy.html by extract.js. Do not edit here — edit the prototype and re-run `npm run extract`. The prototype's mount() call is redirected to SeedlingRenderer.registerSpecies via the local shim. */ (function () { const mount = (spec) => SeedlingRenderer.registerSpecies(1, spec); /* Field Poppy — Papaver rhoeas. A leafy clump of deeply pinnately-lobed (fern-like) leaves, out of which slender hairy stems BOLT and each runs its own lifecycle: a nodding green bud → straightens → 4 papery scarlet petals with a dark blotch + stamen ring → a ribbed pepper-pot seed CAPSULE. An older plant carries all stages at once. Coherent WIND leans the whole plant. Monocarpic. Scatters in drifts → a rare Field carpet. Ported to the shared core. */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='poppy'); mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Field Poppy', sub:'papaver v2', senFrom:0.80, note:'Lobed fern-like rosette · slender hairy stems bolt and bloom · nodding bud → 4 papery scarlet petals → ribbed capsule (all stages at once) · wind-leaned · scatters into drifts/fields.', PAL:{ soil:'#6f5128', dry:'#7a5a30', stem:'#5E7B3A', stemDry:'#9A8A52', leafDark:'#46662F', leafMid:'#6E8B4E', leafLight:'#8BA86A', petal:'#E0382C', petal2:'#C42718', petalEdge:'#F0664E', blotch:'#2A0E07', center:'#1D1208', stamen:'#241606', bud:'#7E924E', capsule:'#8FA05C', capsuleDry:'#A6924E', pore:'#241a0c' }, stageName(st,env){ if(st.diedAt>0)return'Sealed'; const m=env.m; if(env.ageF>0.85)return'Senescent'; if(m<0.12)return'Sprout'; if(m<0.40)return'Rosette'; if(m<0.58)return'Budding'; if(m<0.80)return'Blooming'; return'Seeding'; }, draw(st, C, env){ const {m,v,sen,season,browny,density,rewards,ageF,baseX,baseY}=env, P=this.PAL; const bgC = BACKDROPS[backdropFor(st.seed)]; const seasonBloom = season==='Summer'?1 : season==='Spring'?0.9 : season==='Autumn'?0.7 : 0.55; function poppyDNA(seed){ const dd=rng('dna',seed), g=()=>(dd()+dd()+dd()-1.5)/1.5; return { stems:0.7+0.9*dd(), spread:26+26*dd(), stature:0.84+0.30*dd(), waviness:0.5+1.0*dd(), petalRag:0.10+0.30*dd(), petalHue:g()*0.5, leafCut:0.55+0.45*dd(), bristly:0.5+0.5*dd(), windDir:dd()<0.5?1:-1, windStr:Math.pow(dd(),2.3) }; } // ===================== one poppy plant (repeated to scatter into a drift/field) ===================== function paintPoppy(I){ const {seed, artSeed, cx, by, S, fade, m, density, v, sen, browny, rewards} = I; const FC = c => fade>0 ? mix(c, bgC, fade) : c; const ra=rng('struct',artSeed), D=poppyDNA(seed); const stemC=FC(mix(P.stem,P.stemDry,Math.max((1-v)*0.5,sen))); const leafD=FC(mix(P.leafDark,P.stemDry,browny)), leafM=FC(mix(P.leafMid,P.stemDry,browny)), leafL=FC(mix(P.leafLight,P.stemDry,browny*0.9)); const petalC=FC(mix(P.petal,P.petal2,clamp(-D.petalHue,0,1)*0.7)), petalWarm=FC(mix(mix(P.petal,P.petal2,clamp(-D.petalHue,0,1)*0.7),'#F08A3C',clamp(D.petalHue,0,1)*0.6)); const capC=FC(mix(P.capsule,P.capsuleDry,Math.max(sen,browny*0.55))); const greenOf=rs=>shade(mix(leafM, rs()<0.5?leafD:leafL, rs()),rs); const windLean=D.windDir*D.windStr*0.42, rootX=cx - D.windDir*D.windStr*46*S; function lobedLeaf(bx,byy,ang,len,rl){ const steps=Math.max(3,Math.ceil(len/GRID)), droop=clamp(sen+(1-v)*0.4,0,1)*0.7; let dx=Math.cos(ang),dy=Math.sin(ang); dx=lerp(dx,0,droop); dy=lerp(dy,1,droop); const n=Math.hypot(dx,dy)||1; dx/=n; dy/=n; let dir=Math.atan2(dy,dx), pos=[bx,byy]; for(let s=1;s<=steps;s++){ const u=s/steps; dir += Math.sin(u*3.0+rl()*6)*0.04; const cd=[Math.cos(dir),Math.sin(dir)]; pos=[pos[0]+cd[0]*GRID, pos[1]+cd[1]*GRID]; C.push(pos[0],pos[1], 42, dirSym(cd[0],cd[1]), shade(leafM,rl), 'leaves'); const px=-cd[1], py=cd[0], ev=Math.sin(Math.PI*Math.min(u,0.999)), lobeLen=GRID*(0.8+(2.4*D.leafCut)*ev); if(s%2===0 && ev>0.18){ for(const sd of [1,-1]){ let la=Math.atan2(py*sd,px*sd); la += angTo(la,dir)*0.30; const ll=lobeLen*(0.7+0.5*rl()), lsteps=Math.max(1,Math.round(ll/GRID)); for(let t=1;t<=lsteps;t++){ const lp=[pos[0]+Math.cos(la)*GRID*t, pos[1]+Math.sin(la)*GRID*t], edge=t>=lsteps; C.push(lp[0],lp[1], 41, edge?'X':dirSym(Math.cos(la),Math.sin(la)), edge?shade(leafD,rl):greenOf(rl), 'leaves'); } } } } } const foliageF=(1-0.5*sen), axisH=(78+150*m)*(0.85+0.30*D.stature)*foliageF*S; { const rl=rng('axis',seed); let dir=UP+windLean*0.4, pos=[rootX,by-4]; const steps=Math.max(2,Math.ceil(axisH/GRID)); for(let s=1;s<=steps;s++){ const u=s/steps; dir += Math.sin(u*2+rl()*3)*0.02 + windLean*0.03*u; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1],46,dirSym(Math.cos(dir),Math.sin(dir)),shade(stemC,rl),'stem'); } } const nFolF=clamp(3 + density*6*foliageF + (rng('nl',seed)()*2-1)*0.6, 2.5, 10), nFol=Math.ceil(nFolF); for(let i=0;i<nFol;i++){ const rl=rng('leaf',seed,i), side=i%2?1:-1, emerge=i===nFol-1?clamp(nFolF-(nFol-1),0.2,1):1, u=(nFol>1? i/(nFol-1):0); const nodeX=rootX + windLean*38*u*S + (rl()*2-1)*5, nodeY=by-4 - u*axisH*0.96, spread=lerp(1.18,0.46,u); const ang=UP + side*spread + windLean*0.3 + (rl()*2-1)*0.12, len=(46+78*m)*lerp(1.15,0.5,u)*(0.78+0.34*rl())*emerge*S; lobedLeaf(nodeX,nodeY,ang,len,rl); } const boltF=smooth(0.24,0.62,m), nStemF=clamp(boltF*(0.8 + density*4.2*D.stems) + (rng('ns',seed)()*2-1)*0.4, 0, 5), nStem=Math.ceil(nStemF); function growStem(sx, hLen, nod, rs){ const steps=Math.max(3,Math.ceil(hLen/GRID)), wob=(rs()*2-1)*0.6, ph=rs()*6.28, aim=UP+windLean*0.7; let dir=UP+windLean*0.5, pos=[sx,by-4]; for(let s=1;s<=steps;s++){ const u=s/steps, exposure=clamp((by-pos[1])/520,0,1); dir += angTo(dir,aim)*0.06 + Math.sin(u*5+ph+wob*6)*0.009*D.waviness + sen*0.045*exposure; if(nod>0 && u>0.66) dir += angTo(dir,DOWN)*nod*0.17*smooth(0.66,1,u); const cd=[Math.cos(dir),Math.sin(dir)]; pos=[pos[0]+cd[0]*GRID, pos[1]+cd[1]*GRID]; C.push(pos[0],pos[1], 50, dirSym(cd[0],cd[1]), shade(stemC,rs), 'stem'); if(s%3===0 && rs()<0.5*D.bristly){ const px=-cd[1], py=cd[0], sd=rs()<0.5?1:-1; C.push(pos[0]+px*sd*GRID, pos[1]+py*sd*GRID, 48, '-', shade(leafD,rs), 'leaves'); } }