memoscan
← all memos

Memo 0x9541e806…198cfc on Ethereum

from a base point. Outline sits a touch higher z so it cuts over the petal behind. -- function petal(bx,by,ang,len,halfW,z,ez,pal,rs){ const nx=Math.cos(ang), ny=Math.sin(ang), px=-ny, py=nx, tn=Math.max(3,Math.ceil(len/GRID)); for(let t=1;t<=tn;t++){ const u=t/tn; const taper=1-smooth(0.5,1.0,u); // taper over the top half const hw=halfW*Math.min(1, 2.2*Math.sin(Math.PI*u*0.5))*taper*taper; // broad lower body → pointed lanceolate tip (lotus) if(hw<GRID*0.32 && u>0.5) continue; const ox=bx+nx*GRID*t, oy=by+ny*GRID*t; for(let w=-hw;w<=hw;w+=GRID){ const aw=Math.abs(w); const edge = aw>hw-GRID*1.5 || (u>0.78 && hw<halfW*0.5); // outline: sides + the pointed tip const vein = aw<GRID*0.7 && u<0.85; // central vein const grad = u<0.28?pal.base : u<0.62?mix(pal.base,pal.body,smooth(0.28,0.62,u)) : mix(pal.body,pal.tip,smooth(0.62,1,u)); const col = edge?pal.edge : vein?mix(pal.body,pal.edge,0.4) : grad; // outlines go to a HIGH shared z (ez) so EVERY petal boundary renders over the fills → petals read as separate C.push(ox+px*w, oy+py*w, edge?ez:z, '+', shade(FC(col),rs),'flowers'); } } } // a SIDE-VIEW lotus: an egg/goblet of pointed petals opening upward from the stem tip, // top petals longest (tall egg), reflexed lower petals, paler inner cup, gold stamen centre. function drawBloom(fx,fy,R,open,rs){ const PB ={base:PV.lt, body:mix(PV.pt,PV.lt,0.45), tip:PV.dk, edge:PV.vn}; // outer: pale body, saturated tip, dark rim (per variety) const PBi={base:'#ffffff', body:mix(PV.lt,'#ffffff',0.35), tip:PV.pt, edge:PV.dk}; // inner cup, paler // SIDE VIEW: the flower is a CUP / egg opening UPWARD. Petals fan UP & OUT from a NARROW base just // above the stem (which hides behind them) — NONE point straight down (that would be a top view). // A shorter, PALER FRONT whorl sits in front of the centre so we read the cup's DEPTH; the golden // stamens nestle LOW in the middle of the cup. Broad pointed petals overlap into a full flower. const fcx=fx, fcy=fy - R*0.14, ringR=R*0.15; const sp=lerp(1.04,1.38,open); const bN=7; for(let k=0;k<bN;k++){ const t=(k/(bN-1))*2-1, ang=UP+t*sp; // BACK whorl: tall outer-face cup-wall petals (behind) petal(fcx+Math.cos(ang)*ringR, fcy+Math.sin(ang)*ringR, ang, R*(1.10-0.16*Math.abs(t)), R*0.245, 66+Math.round(Math.abs(t)*2), 82, PB, rs); } const fN=6; for(let k=0;k<fN;k++){ const t=((k+0.5)/fN)*2-1, ang=UP+t*lerp(0.60,1.04,open); // FRONT whorl: shorter, PALER inner-face petals, offset between, in FRONT → cup depth petal(fcx+Math.cos(ang)*ringR*0.66, fcy+Math.sin(ang)*ringR*0.66, ang, R*(0.76-0.12*Math.abs(t)), R*0.215, 74+Math.round((1-Math.abs(t))*2), 81, PBi, rs); } // ---- the golden POLLEN crown in the MIDDLE: a ring of stamens (filament + anther) radiating // from a small central receptacle, surrounded by the petals (the lotus signature). ---- const ccx=fcx, ccy=fcy - R*0.30, baseR=R*0.075; // pollen crown LOW in the middle of the cup const nStam=Math.round(46*(0.6+0.4*open)); // dense → a fuzzy golden cluster, not a sparse star for(let k=0;k<nStam;k++){ const sr=rng('st',Math.round(fx),Math.round(fy),k); const a=(k/nStam)*TAU + (sr()*2-1)*0.22; const up=(-Math.sin(a)+1)/2; // 1 at top → 0 at bottom const r0=baseR*(0.4+1.0*sr()); // start radius jitter → fuzzy core, no clean spokes const len=R*(0.085+0.085*sr())*(0.55+0.55*up)*(0.7+0.5*open); // upper stamens longer → a crown, not a flat disc const f0x=ccx+Math.cos(a)*r0, f0y=ccy+Math.sin(a)*r0, steps=Math.max(1,Math.ceil(len/GRID)); for(let t=1;t<=steps;t++) C.push(f0x+Math.cos(a)*GRID*t, f0y+Math.sin(a)*GRID*t, 84, '.', shade(FC(mix(P.stamen,P.center,sr()*0.5)),sr),'flowers'); C.push(f0x+Math.cos(a)*len, f0y+Math.sin(a)*len, 85,'.',shade(FC(P.centerDk),sr),'flowers'); // anther tip (pollen) } // central receptacle dome (the seed-pod head), on top of the stamens const cw=R*0.13*(0.7+0.3*open), ch=cw*0.85; for(let gx=-cw;gx<=cw;gx+=GRID)for(let gy=-ch;gy<=ch;gy+=GRID){ if((gx*gx)/(cw*cw)+(gy*gy)/(ch*ch)>1)continue; const top=clamp(-gy/ch*0.5+0.5,0,1); C.push(ccx+gx,ccy+gy,87,'+',shade(FC(mix(P.centerDk,P.center,top)),rs),'flowers'); } for(let k=0;k<5;k++){ const a=k/5*TAU; C.push(ccx+Math.cos(a)*cw*0.42, ccy+Math.sin(a)*ch*0.42, 88,'O',shade(FC(mix(P.centerDk,'#5e4e1c',0.45)),rs),'flowers'); } } // young = a tight GREEN egg (sepal cover); as it ripens the coloured petals BREAK out of the tip // (colour spreads down from the top) and tips poke out — then it opens into the bloom. // `ripe` 0→1 = green→coloured/about-to-open · `grow` 0→1 = young round egg → elongated bud. function drawBud(fx,fy,R,lean,ripe,grow,rs){ const a=UP+lean, nx=Math.cos(a), ny=Math.sin(a), px=-ny, py=nx; const len=R*(1.3+0.9*grow), maxW=R*0.58, tn=Math.max(3,Math.ceil(len/GRID)); let p=[fx,fy]; const grA=mix(P.padDk,'#46663c',0.25), grB=mix(P.pad,P.padLt,0.35), grE=P.padEdge; // green sepal cover for(let t=1;t<=tn;t++){ p=[p[0]+nx*GRID, p[1]+ny*GRID]; const u=t/tn; const hw=maxW*Math.pow(Math.sin(Math.PI*clamp(u*0.62+0.12,0,1)),0.60)*(1-0.85*smooth(0.55,1,u)); // egg: round fat base → pointed tip if(hw<GRID*0.3 && u>0.55) continue; for(let w=-hw;w<=hw;w+=GRID){ const aw=Math.abs(w), seam=aw<GRID*0.7, e=aw>hw-GRID*1.1; const pink=clamp(ripe*1.5 - (1-u)*0.95, 0, 1); // colour breaks at the TIP first, spreads down const gcol = e?grE : seam?mix(grA,grE,0.5) : (w<0?grA:grB); const pcol = e?PV.dk : seam?PV.vn : (w<0?PV.pt:PV.lt); C.push(p[0]+px*w, p[1]+py*w, 70, '.', shade(FC(mix(gcol,pcol,pink)),rs),'flowers'); } } if(ripe>0.45){ const tipN=2+Math.round(ripe*3), tbx=fx+nx*len*0.82, tby=fy+ny*len*0.82; // coloured petal tips poking from the top for(let k=0;k<tipN;k++){ const tt=(tipN>1?(k/(tipN-1)-0.5):0)*lerp(0.25,0.9,ripe), ta=a+tt, tlen=R*(0.18+0.5*ripe); let pp=[tbx,tby]; const ptn=Math.max(2,Math.ceil(tlen/GRID)); for(let s=1;s<=ptn;s++){ pp=[pp[0]+Math.cos(ta)*GRID, pp[1]+Math.sin(ta)*GRID]; const pu=s/ptn; C.push(pp[0],pp[1],71, pu>0.7?'.':dirSym(Math.cos(ta),Math.sin(ta)), shade(FC(mix(PV.pt,PV.dk,pu*0.4)),rs),'flowers'); } } } } function drawPod(fx,fy,R,dryF,lean,rs){ // the iconic flat-topped "showerhead" receptacle const topW=R*1.08, topH=R*0.40, bodyH=R*0.66, lx=Math.sin(lean)*bodyH*0.5; const cTop=mix(P.pod,P.podDry,dryF), cSide=mix(P.podRim,P.podDryDk,dryF), cFace=mix(mix(P.pod,P.podDry,dryF),'#000000',0.10); const rows=Math.ceil(bodyH/GRID); for(let r=0;r<=rows;r++){ const u=r/rows, wdt=lerp(topW*0.92, topW*0.34, Math.pow(u,0.8)), yy=fy+topH*0.55+u*bodyH, xx=fx+lx*u; for(let gx=-wdt;gx<=wdt;gx+=GRID){ const e=Math.abs(gx)>wdt-GRID*1.1; C.push(xx+gx,yy,80, e?dirSym(0,1):'\\', shade(FC(e?mix(cSide,'#000000',0.15):cSide),rs),'flowers'); } } for(let gx=-topW;gx<=topW;gx+=GRID)for(let gy=-topH;gy<=topH;gy+=GRID){ if((gx*gx)/(topW*topW)+(gy*gy)/(topH*topH)>1)continue; const e=(gx*gx)/(topW*topW)+(gy*gy)/(topH*topH)>0.8; C.push(fx+gx,fy+gy,83,'-',shade(FC(e?cSide:cFace),rs),'flowers'); } function pit(ox,oy){ C.push(ox,oy,85,'O',shade(FC(mix(P.podRim,'#000000',0.1+0.2*dryF)),rs),'flowers'); C.push(ox,oy,86,'.',shade(FC(P.pit),rs),'flowers'); } const nOut=Math.round(7+dryF*3); for(let k=0;k<nOut;k++){ const a=k/nOut*TAU + rs()*0.2; pit(fx+Math.cos(a)*topW*0.62, fy+Math.sin(a)*topH*0.62); } for(let k=0,nIn=Math.round(3+dryF);k<nIn;k++){ const a=k/nIn*TAU + 0.5; pit(fx+Math.cos(a)*topW*0.30, fy+Math.sin(a)*topH*0.30); } pit(fx,fy); } // small rhizome crown the petioles emerge from (NO long roots) for(let k=0;k<6;k++){ const rs=rng('crown',pseed,k), a=Math.PI + (k/5-0.5)*1.7; C.push(cx+Math.cos(a)*GRID*(1+k%3)*S, by+2+Math.abs(Math.sin(a))*GRID*0.5, 18, dirSym(Math.cos(a),Math.sin(a)), shade(FC(P.stalkDk),rs),'stem'); } // ---- LEAVES: SHORT petioles (heavy pads stay low), wavy pads, 1–3 per plant ---- const nF=clamp(1+density*1.6, 1, 2.4), n=Math.ceil(nF); // 1–2 big distinct leaves (reference shows two) const leafHmax=(55+110*m)*S; // SHORT relative to the heavy leaf (no towering petioles) let tallest=by, padTop=by; for(let i=0;i<n;i++){ const rs=rng('pad',st.seed,pseed,i); const emerge=i===n-1?clamp(nF-(n-1),0.2,1):1, newest=i===n-1 && n>1; const t=n>1? i/(n-1):0.5; const bx=cx+(t-0.5)*120*S+(rs()*2-1)*8*S; // leaves sit apart with a clear gap const hFrac=newest ? 0.64 : (0.88+0.12*rs()); // STAGGER heights → two pads separate, don't form a clover const leafH=leafHmax*hFrac*emerge*collapse + 10; const lean=(t-0.5)*0.55 + (rs()*2-1)*0.05 + windDir*windStr*0.14; // pads ride low on the water → only a slight wind lean const top=rise(bx, by+2, leafH, lean, 22, mix(P.stalk,P.stalkDk,0.25*(1-hFrac)), false, rs); tallest=Math.min(tallest, top[1]); const yellowF=clamp(browny*0.55+sen*0.9+winter*0.7, 0, 1)*(0.6+0.4*(1-i/n)); const ragged=clamp(sen*0.7+winter*0.5-0.15,0,0.6); const z=30+Math.round((1-hFrac)*9); const R=(58+72*m)*emerge*(0.84+0.24*rs())*S*(newest?0.7:1); // BIG pads — lotus leaves are bigger than its flowers drawPad(top[0], top[1], R, z, yellowF, ragged, newest, rs); padTop=Math.min(padTop, top[1]-R*0.80); // visual top of the foliage (for flower-height reference) } // ---- FLOWER stalks: each flower EMERGES as a short budded stem just above the pads, then its // peduncle RISES and the bud OPENS into a bloom (→ pod). Staggered birth maturities mean // flowers appear one at a time and GROW IN — no full-length stem popping out of nowhere. // A well-cared mature root carries up to 3, each at its own phase. ---- // Up to 5 flowers per plant — count = per-seed floriferousness × earned rewards (lots of per-plant // variation). Positions are SPREAD SYMMETRICALLY across the plant's width (never center+left only, // so no side is starved); central blooms emerge first, outer L/R pairs together at higher maturity. const florif = 0.7 + 0.6*rng('florif',st.seed,pseed)(); const nFlow = reproOK ? clamp(Math.round((1 + rewards*3.4)*florif), 1, 5) : 0; const fpos = nFlow>1 ? Array.from({length:nFlow},(_,i)=>(i/(nFlow-1)-0.5)*2) : [0]; // symmetric −1..1 for(let f=0; f<nFlow; f++){ const fside = fpos[f], sgn = fside<0?-1:1; const birthF = 0.50 + Math.abs(fside)*0.20; // centre 0.50 → outer 0.70; L/R pairs emerge together if(!reproOK || m < birthF) continue; const rs=rng('fl',st.seed,pseed,f); // structural draws in FIXED order (frame-stable) const posJit=(rs()*2-1), hr=rs()*(Math.abs(fside)<0.34?1:0.6), leanJit=(rs()*2-1), Rjit=rs(); const fgrow = clamp((m - birthF)/0.18, 0, 1); // 0 = just emerged (short bud) → 1 = full height const fx0 = cx + fside*122*S + posJit*16*S; // blooms fan across the plant's full span const targetH = ((by-padTop) + lerp(18+28*m, 205+225*m, Math.pow(hr,1.9))*S) * (0.85+0.18*v)*standC; const shortH = (by-padTop) + (14+12*m)*S; // just-emerged: a bud peeking above the pads const ph = lerp(shortH, Math.max(shortH,targetH), smooth(0,1,fgrow)); // the peduncle RISES as the flower grows const lean = leanJit*0.10 + fside*0.05 + windDir*windStr*(0.22+0.45*hr)*fgrow; const top = rise(fx0, by+2, ph, lean, 52+f, P.stalk, true, rng('flStem',st.seed,pseed,f)); // stem on its own stream const R = (40+42*m)*S*(0.88+0.14*Rjit); // blooms SMALLER than the pads (lotus proportions) const hRs = rng('flHead',st.seed,pseed,f); // head on its own stream → stable regardless of stem height const localPod = clamp(podDrive + f*0.06 + (dead?1:0), 0, 1); const opening = smooth(0.50,0.92,fgrow) * clamp(bloomDrive*1.7, 0, 1); // opens near full height, gated by season/rewards if(dead || localPod>0.5) drawPod(top[0], top[1], R*0.6, clamp(sen*1.1+(season==='Autumn'?0.3:0)+(dead?0.4:0),0,1), 0.04*sgn, hRs); else if(opening>0.40) drawBloom(top[0], top[1], R, opening, hRs); else { const ripe=clamp(Math.max(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'); }