memoscan
← all memos

Memo 0x2744cd9a…b2a809 on Ethereum

62; // THIN Voronoi boundary -> clean fracture line const edge = y < top + GRID*1.5; // top facet catches light const crev = Math.abs(x-cx) < GRID*1.5 && y < top+52; // central crevice the tuft emerges from let col, sym='.'; if(crev){ col=P.crack; } else if(seam){ col=P.crack; // crack runs perpendicular to the seed-pair axis sym = dirSym(-(b1.y-b0.y), b1.x-b0.x); } else { col = b0.t<0.30?P.rockDk : b0.t<0.68?P.rock : P.rockLt; if(edge) col=mix(col,P.rockLt,0.5); } C.push(x,y, edge?12:(seam?11:8), sym, shade(col,rs), 'season'); if(edge && !seam && snowF>0.2 && rs()<snowF*0.75) C.push(x,y-GRID, 14, '.', shade(rs()<0.6?P.snow:P.snowDk,rs), 'season'); // snow caps the lit edges } } // 3. a few long straight MASTER CRACKS across the faces (fractured-granite feel) for(let kc=0; kc<3+Math.floor(rs()*3); kc++){ let px=FRAME+24+rs()*(W-2*FRAME-48), py=topAt(px)+12+rs()*120; let dir=DOWN+(rs()*2-1)*1.15; const len=44+rs()*120, stp=Math.ceil(len/GRID); for(let s=0;s<stp;s++){ px+=Math.cos(dir)*GRID; py+=Math.sin(dir)*GRID; if(py>baseY+36 || px<FRAME+6 || px>W-FRAME-6) break; C.push(px,py, 11, dirSym(Math.cos(dir),Math.sin(dir)), shade(P.crack,rs), 'season'); } } } rockField(); // panicles bloom GREEN; they turn straw only as they SET SEED — late maturity, autumn, or senescence const seedSet=clamp(smooth(0.80,1.0,m)*0.8 + sen*0.9 + (season==='Autumn'?0.45:0), 0, 1); const bgc=BACKDROPS[backdropFor(st.seed)]; // backdrop colour, for fading distant tufts back const fmix=(c,f)=> f>0 ? mix(c,bgc,f) : c; // ---------- one TUFT (cushion of arching blades + airy panicles); sc=size, fade=distance, tag=identity ---------- function tuft(ox, oy, sc, fade, tag){ const zo = tag==='hero' ? 0 : -13; // companions sit BEHIND the hero const nF=clamp(16+density*74,16,90)*(0.55+0.45*sc), n=Math.ceil(nF); // blade COUNT (cushion lushness) ← entries (density); floor 16 = the survivor always tufts for(let k=0;k<n;k++){ const rs=rng('blade',st.seed,tag,k); const emerge=k===n-1?clamp(nF-(n-1),0.12,1):1; const a0=(rs()*2-1); // stable fan position const ang=UP + a0*1.18; const len=(84+205*m)*sc*(0.5+0.6*rs())*emerge*(1-0.22*Math.abs(a0)); const arch=0.5+0.85*rs(), outer=Math.abs(a0); // outer = older blade const bladeBrown=clamp(smooth(0.62,1.0,outer)*0.9 + browny*0.6, 0, 1); // ONLY outer/older blades straw off let dir=ang, pos=[ox+a0*11*sc, oy]; const steps=Math.max(2,Math.ceil(len/GRID)); for(let i=1;i<=steps;i++){ const u=i/steps; dir += angTo(dir,DOWN)*arch*0.05*(0.3+0.7*u); // arch outward then over the rocks pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; const tipBrown=clamp(bladeBrown*(0.6+0.4*u) + browny*u*0.35, 0, 1); const green=mix(P.blade, rs()<0.5?P.bladeDk:P.bladeLt, rs()); const col=fmix(mix(green, mix(P.straw,P.strawDk,rs()), tipBrown), fade); C.push(pos[0],pos[1], 40+zo, dirSym(Math.cos(dir),Math.sin(dir)), shade(col,rs), 'leaves'); } } function spikelet(x,y,dir,rs){ const col=fmix(mix(P.spikeGreen, P.spikeStraw, clamp(seedSet*(0.55+0.45*rs()),0,1)), fade); const awnCol=fmix(mix(P.spikeGreen, P.awn, clamp(0.35+seedSet,0,1)), fade); let sp=[x,y]; const nn=3+Math.floor(rs()*2); // a small oval lemma cluster for(let i=0;i<nn;i++){ sp=[sp[0]+Math.cos(dir)*GRID*0.9, sp[1]+Math.sin(dir)*GRID*0.9]; C.push(sp[0],sp[1], 92+zo, i%2?'O':'.', shade(col,rs), 'flowers'); } let ap=[sp[0],sp[1]]; // a fine awn extending past the tip for(let i=0;i<3+Math.floor(rs()*3);i++){ ap=[ap[0]+Math.cos(dir)*GRID, ap[1]+Math.sin(dir)*GRID]; C.push(ap[0],ap[1], 90+zo, dirSym(Math.cos(dir),Math.sin(dir)), shade(awnCol,rs), 'flowers'); } } if(flowering>0.08){ const nPan=Math.max(1, Math.round((1+m*4)*flowering*(0.45+0.55*rewards)*(tag==='hero'?1:0.55))); // seed-heads ← rewards (floor keeps the survivor seeding) for(let p=0;p<nPan;p++){ const rs=rng('pan',st.seed,tag,p); const grown=clamp((m-(0.4+p*0.09))/0.12,0,1); // panicles emerge progressively if(grown<=0) continue; const sx=ox+(rs()*2-1)*(20+m*44)*sc; const sh=(190+260*m)*sc*grown*(0.7+0.4*rs()); let pos=[sx,oy-4], dir=UP+(rs()*2-1)*0.2; const steps=Math.ceil(sh/GRID); for(let s=1;s<=steps;s++){ const u=s/steps; dir += Math.sin(u*3+rs()*6)*0.02 + (rs()*2-1)*0.004; // wiry wobble pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1], 70+zo, dirSym(Math.cos(dir),Math.sin(dir)), shade(fmix(P.stalk,fade),rs), 'stem'); if(u>0.5 && s%3===0){ // open panicle branches in the upper half const nb=1+Math.floor(rs()*2); for(let b=0;b<nb;b++){ const side=rs()<0.5?1:-1; const bl=(16+30*m)*sc*(1-u)*(0.6+0.6*rs()); let bp=[pos[0],pos[1]], bd=dir+side*(0.6+0.5*rs()); const bst=Math.max(2,Math.ceil(bl/GRID)); for(let bs=1;bs<=bst;bs++){ bd += angTo(bd, UP+side*0.3)*0.1; // branch arcs up bp=[bp[0]+Math.cos(bd)*GRID, bp[1]+Math.sin(bd)*GRID]; C.push(bp[0],bp[1], 71+zo, dirSym(Math.cos(bd),Math.sin(bd)), shade(fmix(P.stalk,fade),rs), 'stem'); } spikelet(bp[0],bp[1],bd,rs); } } } spikelet(pos[0],pos[1],dir,rs); // a terminal spikelet } } } // ---------- a small COLONY: companions scatter in the crevices behind the hero (varies by seed; not always solo) ---------- const cr=rng('colony',st.seed); const nComp=[0,1,1,2,2,3][Math.floor(cr()*6)]; for(let i=0;i<nComp;i++){ const side=cr()<0.5?-1:1, depth=cr(); // depth 0 = near ... 1 = far const bx=clamp(cx + side*(78+cr()*210), FRAME+72, W-FRAME-72); const by=(baseY-150) + cr()*46; // rooted lower on the rock surface than the hero tuft(bx, by, lerp(0.62,0.38,depth), lerp(0.40,0.64,depth), 'bg'+i); } tuft(cx, cy, 1, 0, 'hero'); // the hero tuft, full size and in front // ---------- SNOW: scattered flecks (more in winter), settled + drifting ---------- const sr=rng('snow',st.seed); const nSnow=Math.round(70*snowF + 14); for(let i=0;i<nSnow;i++){ const x=FRAME+8 + sr()*(W-2*FRAME-16); const y=lerp(baseY+10, 140, sr()*sr()); // biased low (settled on the ground/rocks) C.push(x,y, 96, '.', shade(sr()<0.5?P.snow:P.snowDk, sr), 'flowers'); } }, traits(st,env){ const season=seasonOf(st.now); const snow = season==='Winter'?'Snowfall' : (season==='Summer'?'Clear':'Dusting'); return [['Habitat','Rock crevice'], ['Frost', st.diedAt>0?'Frozen':snow], ['Hardiness','Extreme']]; } }); })(); ; /* species/lavender.js */ /* lavender — species index 3. GENERATED from prototypes/lavender.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(3, spec); /* Lavender — Lavandula. A radiating fountain of fine woody stems (a rounded clump), narrow opposite grey-green leaves, and vertical violet FLOWER-SPIKES of whorled florets held above the foliage. Hardy Mediterranean shrub: slow to wilt, summer bloom. Stems emerge progressively — old stems splay outward, the newest keep rising from the centre. Death = grey dried wands. Grows in a patch (Setting: Solo / Pair / Cluster / Field). Ported to the shared core. */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='lavender'); mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Lavender', sub:'lavandula v2', senFrom:0.80, schemeExempt:[], // spikes follow the per-seed scheme (woven, like the rest) LW:{season:1.0, stem:1.05, leaves:1.1, flowers:1.9}, LS:{season:1.0, stem:1.0, leaves:0.9, flowers:0.8}, note:'Hardy Mediterranean clump · radiating woody stems · narrow opposite leaves · violet flower-spikes above the foliage · summer bloom · death = grey dried wands.', PAL:{ soil:'#6f5128', grey:'#9a9180', flower:'#7A4FC0', flowerLt:'#AE8FE2', flowerDk:'#553399', bud:'#7E8C56', leafDark:'#566E50', leaf:'#88A07E', leafLt:'#B4C3A4', stem:'#6E7A54' }, stageName(st,env){ if(st.diedAt>0)return'Sealed'; const m=env.m; if(env.ageF>0.86)return'Spent'; if(m<0.12)return'Sprout'; if(m<0.42)return'Young bush'; if(m<0.6)return'Bush'; return (env.season==='Summer'||env.season==='Spring')?'Flowering':'Bush'; }, draw(st, C, env){ const {m,v,sen,season,browny,density,rewards,baseX,baseY}=env, P=this.PAL; const bgC = BACKDROPS[backdropFor(st.seed)]; const seasonOpen = season==='Summer'?1 : season==='Spring'?0.8 : season==='Autumn'?0.55 : 0.4; // ===================== one lavender bush (repeated to form a patch) ===================== function paintBush(I){ const {cx, by, S, fade, m, v, sen, browny, density, rewards, zb, pseed} = I; const FC = c => fade>0 ? mix(c, bgC, fade) : c; const flowering = smooth(0.40,0.62,m) * rewards * (1-sen*0.8); const cFlower=FC(mix(P.flower,P.grey,browny)), cFlowerLt=FC(mix(P.flowerLt,P.grey,browny)), cFlowerDk=FC(mix(P.flowerDk,P.grey,browny)); const cBud=FC(mix(P.bud,P.grey,browny*0.9)); const leafD=FC(mix(P.leafDark,P.grey,browny)), leafM=FC(mix(P.leaf,P.grey,browny)), leafL=FC(mix(P.leafLt,P.grey,browny*0.9)); const stemC=FC(mix(P.stem,P.grey,Math.max((1-v)*0.5,sen))); const greenOf=rs=>shade(mix(leafM, rs()<0.5?leafD:leafL, rs()), rs); // per-seed personality so two bushes differ structurally (not just noise) const hero=pseed==='hero'; const dd= hero?rng('dna',st.seed):rng('dna',st.seed,pseed); const D={ spread:0.50+0.55*dd(), lean:(dd()*2-1)*0.16, fanBase:16+30*dd(), vigor:0.85+0.30*dd(), bushiness:dd(), openBias:0.85+0.30*dd(), spikeF:0.80+0.40*dd() }; const MAXS=30, growWin=0.12, splayOpen=smooth(0.0,0.34,m); const lenBase=(180+440*m)*(0.72+0.28*v)*D.vigor*S; for(let k=0;k<MAXS;k++){ const rs= hero?rng('stem',st.seed,k):rng('stem',st.seed,pseed,k); const birthM=(k/MAXS)*0.70; const grown=clamp((density-birthM)/growWin, k===0?0.2:0, 1); // stem COUNT ← density; first always present if(grown<=0) continue; const outerness=lerp(1.0,0.22,k/MAXS); // OLD outer · NEW central → fresh growth from the middle const a0=(rs()*2-1)*outerness*splayOpen; const ang0=UP + a0*D.spread + D.lean; const baseX0=cx + a0*D.fanBase*S; const maxLen=lenBase*(1-0.30*Math.abs(a0))*(0.8+0.4*rs()); const len=maxLen*grown; const zf=rs()<0.5, stemZ=(zf?42:30)+zb, leafZ=(zf?44:28)+zb, flZ=(zf?94:84)+zb; const stemFlower = flowering>0.05 && grown>0.4 && rs() < (0.3+0.66*flowering); const leafTop = stemFlower?0.58:0.86; let dir=ang0, pos=[baseX0, by-3]; const steps=Math.max(3,Math.ceil(len/GRID)); let tip=[pos[0],pos[1]], tdir=dir; for(let i=1;i<=steps;i++){ const u=i/steps; dir += angTo(dir,ang0)*0.05 + Math.sin(i*0.5+rs()*6)*0.009 + a0*0.004*u; const dx=Math.cos(dir),dy=Math.sin(dir); pos=[pos[0]+dx*GRID, pos[1]+dy*GRID]; tip=[pos[0],pos[1]]; tdir=dir; C.push(pos[0],pos[1], stemZ, dirSym(dx,dy), shade(stemC,rs), 'stem'); if(u<leafTop && i%2===0 && rs() < (u<0.28?0.45:0.85)){ // OPPOSITE linear leaves, airier near the base for(const sd of [1,-1]){ const la=dir + sd*(0.5+0.18*rs()) - 0.05; const ll=2+Math.floor((2+m*3)*S*(0.6+0.5*rs())); let lp=[pos[0],pos[1]]; for(let kk=1;kk<=ll;kk++){ const ax=Math.cos(la),ay=Math.sin(la); lp=[lp[0]+ax*GRID,lp[1]+ay*GRID]; const lt=kk>=ll-1; C.push(lp[0],lp[1], leafZ+(lt?2:0), dirSym(ax,ay), lt?shade(leafD,rs):greenOf(rs), 'leaves'); } } } } if(stemFlower){ // tip FLOWER-SPIKE — develops with the stem's own age const stemOpen=smooth(0.4,1.0,grown); const open=clamp(smooth(0.18,0.82,flowering)*D.openBias*seasonOpen*stemOpen,0,1); const sLen=(30+66*m)*smooth(0.05,0.7,flowering)*D.spikeF*stemOpen*(0.72+0.5*rs())*S; const sW=GRID*(1.5+0.8*rs())*Math.sqrt(S), sst=Math.max(3,Math.ceil(sLen/GRID)); let sp=[tip[0],tip[1]], d=tdir; for(let i=1;i<=sst;i++){ const u=i/sst; d += angTo(d,UP)*0.16; sp=[sp[0]+Math.cos(d)*GRID, sp[1]+Math.sin(d)*GRID]; const taper=Math.min(1,u*4)*(1-Math.pow(u,1.7)), hw=sW*taper, px=-Math.sin(d), py=Math.cos(d); for(let w=-hw; w<=hw; w+=GRID){ if(rs()<0.12) continue; const fx=sp[0]+px*w, fy=sp[1]+py*w, localOpen=clamp(open*(1.25-u),0,1), isOpen=rs()<localOpen*0.85; const col = isOpen ? shade(mix(cFlower, cFlowerLt, rs()*0.7), rs) : shade(mix(cBud, cFlowerDk, localOpen), rs); const sym = isOpen ? (rs()<0.5?'+':'X') : (rs()<0.6?'.':'O'); C.push(fx, fy, flZ, sym, col, 'flowers'); } } } } } // ===================== SETTING: a patch — Solo / Pair / Cluster / Field ===================== const sr=rng('setting',st.seed), roll=sr(); const nC = roll<0.18?0 : roll<0.50?1 : roll<0.82?2 : 3+Math.floor(rng('fn',st.seed)()*2); const comps=[]; for(let i=0;i<nC;i++){ const rc=rng('comp',st.seed,i); const side=(i%2===0?1:-1), f=i/Math.max(1,nC); const scale=lerp(0.72,0.46,f)*(0.9+0.2*rc()), off=side*(80+i*46+rc()*46), raise=(34+i*24+rc()*30); comps.push({cx:baseX+off, by:baseY-raise, S:scale, fade:lerp(0.5,0.74,f), m, v, sen, browny, density:clamp(density+(rc()*2-1)*0.12,0.05,1), rewards, zb:-10, pseed:'c'+i, depth:f}); } comps.sort((a,b)=>b.depth-a.depth).forEach(paintBush); // far (faint) → near paintBush({cx:baseX, by:baseY, S:1.0, fade:0, m, v, sen, browny, density, rewards, zb:0, pseed:'hero'}); }, traits(st,env){ const sr=rng('setting',st.seed), roll=sr(); const setting = roll<0.18?'Solo' : roll<0.50?'Pair' : roll<0.82?'Cluster' : 'Field'; return [ ['Setting', setting], ['Stems', clamp(Math.round(1+env.density*14),1,15)], ['Stature', Math.round((110+230*env.m)*(0.7+0.3*env.v))+' px'], ]; } })); })(); ; /* species/lotus.js */ /* lotus — species index 10. GENERATED from prototypes/sacred-lotus.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(10, spec); /* Sacred Lotus — Nelumbo nucifera. An aquatic plant: large peltate pads with a WAVY / undulate margin and radial veins, held on SHORT petioles low over the water (heavy leaves don't reach far up; the newest leaf is a small cupped emerging blade), and a solitary red-pink BLOOM raised above the foliage on its own taller peduncle — broad layered petals around a golden receptacle with a fringe of stamens. Lifecycle on the peduncle: nodding BUD → open BLOOM → green POD → dry "showerhead" seed pod. Lotuses often grow in a stand: a seed-driven COLONY renders 1 → many plants (solo to a pond bunch). Summer bloom, winter pad-dieback. */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='lotus'); mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Sacred Lotus', sub:'nelumbo v4', senFrom:0.82, note:'Aquatic · big wavy pads low on the water, side-view cup blooms (pointed petals + golden stamen crown) on stems of varied height → showerhead seed pod · flower varieties Pink / White / Rose · coherent WIND bends the stalks downwind · 1–5 flowers per root, spread both sides · solo to a full pond bunch scattered across the water.', PAL:{ soil:'#6a7a5a', stalk:'#6E9A6B', stalkDk:'#4C7754', speck:'#365E3C', pad:'#6E9A5E', padDk:'#3E6B43', padLt:'#9CC07E', padVein:'#33572F', padEdge:'#274A29', padYellow:'#C7A24E', petal:'#E0796E', petalLt:'#F7D6CE', petalDk:'#C53A30', petalVein:'#962017', center:'#E8B23A', centerDk:'#B07A1E', stamen:'#EFC85C', pod:'#7E9A55', podRim:'#5A7A3C', podDry:'#8A714A', podDryDk:'#5A4730', pit:'#3E2E1A' }, stageName(st,env){ if(st.diedAt>0)return'Dry pod'; const m=env.m; if(m<0.15)return'Sprout'; if(m<0.50)return'Floating pads'; if(m<0.72)return'Budding'; return'Bloom'; }, draw(st, C, env){ const {m,v,sen,season,browny,density,rewards,reproOK,dormancy,dead,baseX,baseY}=env, P=this.PAL; const winter=dormancy||0; const bgC = BACKDROPS[backdropFor(st.seed)]; const waterY = baseY - 8; // plants root at the bottom (like the other species), grow UP from here const seasonBloom = season==='Summer'?1 : season==='Spring'?0.7 : season==='Autumn'?0.45 : 0.30; // monsoon bias, floored // per-seed POND environment, shared by every plant in the colony const windDir = rng('windD',st.seed)()<0.5?-1:1; const windStr = Math.pow(rng('windS',st.seed)(),2.6); // 0..1, skewed LOW → mostly calm, rarely a storm function varietyPal(key){ const x=rng('variety',st.seed,key)(); // per-PLANT flower colour (so a pond can mix pink + white) if(x<0.42) return {name:'Pink', lt:'#F7D6CE', pt:'#E0796E', dk:'#C53A30', vn:'#962017'}; if(x<0.80) return {name:'White', lt:'#FFFFFF', pt:'#F6F1EA', dk:'#EBA48C', vn:'#C56A50'}; // white petals, soft rose-flushed tips + rim (reads white & pops on cream) return {name:'Rose', lt:'#F3C4D4', pt:'#D85A7A', dk:'#A82A52', vn:'#7C1B3C'}; } // ===================== one lotus plant (repeated to form a colony) ===================== function paintLotus(I){ const {cx, by, S, fade, m, v, sen, density, rewards, reproOK, browny, dead, pseed} = I; const FC = c => fade>0 ? mix(c, bgC, fade) : c; const PV = varietyPal(pseed); // this plant's petal palette (Pink / White / Rose) const collapse = clamp(1-0.45*sen-0.42*winter, 0.18, 1); // pads sink & yellow as they die back const standC = clamp(1-0.12*sen-0.42*winter, 0.55, 1); // woody flower/pod stalks stay STANDING const bloomDrive = reproOK ? clamp(rewards*(0.55+0.45*seasonBloom)*(1-sen),0,1) : 0; const podDrive = reproOK ? clamp(sen*1.2 + (season==='Autumn'?0.35:0) + smooth(0.82,1.0,m)*0.5, 0,1) : 0; // -- petiole / peduncle: erect at the base, GENTLY arcing toward `lean` near the top, with a // faint sway — so stems read natural (not ramrod-straight parallel poles). -- function rise(x0,y0,h,lean,z,col,wideF,rs){ const steps=Math.max(2,Math.ceil(h/GRID)); let pos=[x0,y0], dir=UP; const sway=(rs()*2-1), swF=0.7+rs()*0.8; for(let s=1;s<=steps;s++){ const u=s/steps; const target=UP + lean*smooth(0,1,u); // straight low → curving over near the flower dir += angTo(dir, target)*0.08 + Math.sin(u*Math.PI*swF)*0.006*sway; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; const px=-Math.sin(dir), py=Math.cos(dir); C.push(pos[0],pos[1],z,'|',shade(FC(col),rs),'stem'); if(wideF) C.push(pos[0]+px*GRID,pos[1]+py*GRID,z,'|',shade(FC(mix(col,P.stalkDk,0.35)),rs),'stem'); if(s%4===0) C.push(pos[0]+px*(rs()<0.5?GRID:-GRID), pos[1]+py*(rs()<0.5?GRID:-GRID), z,'.', shade(FC(P.speck),rs),'stem'); } return pos; } // -- WAVY peltate pad: undulate margin (not a clean circle) + radial veins + sinus notch -- function drawPad(px,py,R,z,yellowF,ragged,cupped,rs){ const sq = cupped? 0.60 : 0.84; const seam = rng('pr',Math.round(px),Math.round(py),pseed)()*TAU; const Rf = a => R*(1 + 0.14*Math.sin(7*a+seam) + 0.06*Math.sin(3*a-seam*1.3)); // undulate edge const notch = seam + DOWN; // sinus toward the petiole const tone = c => shade(FC(mix(c,P.padYellow,yellowF)),rs); for(let gx=-R*1.25;gx<=R*1.25;gx+=GRID)for(let gy=-R*sq*1.25;gy<=R*sq*1.25;gy+=GRID){ const a=Math.atan2(gy/sq,gx), rr=Math.hypot(gx,gy/sq), re=Rf(a); if(rr>re)continue; const rad=rr/re, cr=rng('pf',Math.round(px),Math.round(gx),Math.round(gy)); if(Math.abs(angTo(a,notch))<0.22 && rad>0.5) continue; // sinus cut if(cr()>0.78) continue; // airy stipple (ground shows through) if(rad>0.6 && ragged>0 && cr()<ragged) continue; // collapsing margin const sheen=Math.sin(a*3+seam)*0.5*(0.6-rad); const col=mix(mix(P.pad, sheen<0?P.padDk:P.padLt, Math.abs(sheen)), P.padDk, smooth(0.7,1.0,rad)*0.3 + (cupped&&gy>0?0.22:0)); C.push(px+gx,py+gy, z, dirSym(gx,gy), tone(col),'leaves'); } const nVein=cupped?9:13; for(let k=0;k<nVein;k++){ const a=seam + k/nVein*TAU; if(Math.abs(angTo(a,notch))<0.22) continue; const ca=Math.cos(a), sa=Math.sin(a)*sq, re=Rf(a); for(let t=R*0.16;t<=re*0.92;t+=GRID){ const gx=ca*t, gy=sa*t; if(ragged>0 && t>re*0.6 && rng('vg',Math.round(px),Math.round(gx),k)()<ragged) continue; C.push(px+gx, py+gy, z+0.6, dirSym(ca,sa), tone(P.padVein),'leaves'); } } for(let a=0;a<TAU;a+=0.045){ if(Math.abs(angTo(a,notch))<0.20) continue; const re=Rf(a), ca=Math.cos(a), sa=Math.sin(a)*sq; if(ragged>0 && rng('eg',Math.round(px),Math.round(a*30))()<ragged*0.8) continue; for(const rf of [0.99,0.92]) C.push(px+ca*re*rf, py+sa*re*rf, z+0.7, dirSym(ca,sa), tone(P.padEdge),'leaves'); } if(cupped) for(let a=Math.PI*1.04;a<=Math.PI*1.96;a+=0.05){ const re=Rf(a), ca=Math.cos(a), sa=Math.sin(a)*sq; // curled upper rim C.push(px+ca*re*0.85, py+sa*re*0.85, z+0.8, dirSym(ca,sa), tone(mix(P.padEdge,'#000000',0.18)),'leaves'); } C.push(px,py,z+0.9,'O',tone(P.padEdge),'leaves'); // peltate hub C.push(px,py,z+1.0,'.',tone(P.padEdge),'leaves'); } // -- one DISTINCT pointed lotus petal: almond shape, dark outline (so petals separate), // central vein, pale base → red tip. `ang` is an absolute direction; the petal grows //