Overlays: add the collapsed double-ring yantra as a layer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01161aC97vUnBnyZRBGyJeHy
This commit is contained in:
teahubxyzandClaude Opus 5.5 committed 2026-10-23 21:52:39 +00:00
1 parent 308c52ba98
commit e8caec019f
1 file changed
+9 -3
+9 -3
View File
@@ -41,6 +41,7 @@ input[type=range]{width:100%;accent-color:var(--fit)}
<button id="l-unit" aria-pressed="false" style="--sw:var(--ring)">inverse · rim</button>
<button id="l-fit" aria-pressed="true" style="--sw:var(--fit)">inverse · ρ</button>
<button id="l-outer" aria-pressed="true" style="--sw:var(--muted)">outer</button>
<button id="l-coll" aria-pressed="false" style="--sw:#c9b8ff">collapsed</button>
</div>
<div class="row">
<label class="sl" for="rho" style="flex:1;min-width:220px">ρ <input id="rho" type="range" min="-1" max="0.6" step="0.001" value="-0.31"> <span class="val" id="rv"></span></label>
@@ -74,9 +75,13 @@ const circ=(r,n=360)=>Array.from({length:n+1},(_,i)=>{const a=i/n*Math.PI*2;retu
function petals(r0,r1,k){const o=[];for(let i=0;i<k;i++){const a0=i/k*Math.PI*2,a1=(i+1)/k*Math.PI*2,am=(a0+a1)/2,pts=[];for(let j=0;j<=40;j++){const t=j/40;const a=a0+(am-a0)*t;const r=r0+(r1-r0)*Math.sin(t*Math.PI/2);pts.push([r*Math.cos(a),r*Math.sin(a)])}for(let j=0;j<=40;j++){const t=j/40;const a=am+(a1-am)*t;const r=r1-(r1-r0)*(1-Math.cos(t*Math.PI/2));pts.push([r*Math.cos(a),r*Math.sin(a)])}o.push(pts)}return o}
function bhupura(){const q=[[1.6,-1.6],[1.6,-0.6],[1.73,-0.6],[1.73,-1.2],[1.97,-1.2],[1.97,1.2],[1.73,1.2],[1.73,0.6],[1.6,0.6],[1.6,1.6]];const corners=[];for(let k=0;k<4;k++){const c=Math.cos(k*Math.PI/2),s=Math.sin(k*Math.PI/2);q.forEach(([x,y])=>corners.push([x*c-y*s,x*s+y*c]))}corners.push(corners[0]);const o=[];for(let i=0;i<corners.length-1;i++)o.push(...seg(corners[i],corners[i+1],120).slice(i?1:0));return o}
const OUTER=[circ(1.2),circ(1.4),circ(1.5),...petals(1,1.2,8),...petals(1.2,1.4,16),bhupura()];
// collapsed double-ring yantra: per triangle [base y, apex y, half-base], all 29 concurrences kept
const CT=[[0.274014950098,-1.0,0.961725432295],[-0.274014950101,1.0,0.961725432294],[-1.000000000068,0.274014950098,0.725985049967],[-0.45197009979,1.000000000089,0.624579035598],[1.000000000089,-0.274014950104,0.725985049989],[0.451970099783,-1.000000000068,0.624579035599],[-0.274014950104,0.451970099783,0.312289517816],[0.038274567697,-0.45197009979,0.413695532108],[-4e-12,-0.274014950101,0.194419326613]];
const CP=[[0,-0.27401495],[0.312289518,-0.27401495],[-0.312289518,-0.27401495]], CR=[0.6127161,0.4154623];
const csides=[];CT.forEach(([b,a,w])=>{const A=[-w,b],B=[0,a],C=[w,b];[[A,B],[B,C],[C,A]].forEach(([p,q])=>csides.push(seg(p,q)))});
const RHO_FIT=Math.sqrt(Math.hypot(...V0)*Math.hypot(...M1));
const inv=(p,r2)=>{const r=p[0]*p[0]+p[1]*p[1];return r<1e-12?null:[r2*p[0]/r,r2*p[1]/r]};
let L={orig:true,unit:false,fit:true,outer:true},rho=RHO_FIT,zoom=1,pan=[0,0];
let L={orig:true,unit:false,fit:true,outer:true,coll:false},rho=RHO_FIT,zoom=1,pan=[0,0];
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
function size(){const d=Math.min(devicePixelRatio,2);cv.width=innerWidth*d;cv.height=innerHeight*d;ctx.setTransform(d,0,0,d,0,0);draw()}
let queued=false;
@@ -92,15 +97,16 @@ function render(){
if(L.unit){ring(1,col.ring);sides.forEach(sd=>path(sd.pts.map(p=>inv(p,1)),col.ring,.7,1.1));O.forEach(o=>path(o.map(p=>inv(p,1)),col.ring,.4,1))}
if(L.fit){ring(rho,col.fit);sides.forEach(sd=>path(sd.pts.map(p=>inv(p,rho*rho)),col.fit,.85,1.2));O.forEach(o=>path(o.map(p=>inv(p,rho*rho)),col.fit,.45,1))}
if(L.orig){sides.forEach(sd=>path(sd.pts,sd.up?col.up:col.down,1,1.3));O.forEach(o=>path(o,col.fg,.45,1));path(circ(1),col.fg,.6,1)}
if(L.coll){const cc='#c9b8ff';csides.forEach(p=>path(p,cc,.9,1.3));CR.forEach(r=>ring(r,cc));path(seg([-1.2,CP[0][1]],[1.2,CP[0][1]],2),cc,1,2.4);ctx.fillStyle=cc;CP.forEach(p=>{ctx.beginPath();ctx.arc(X(p),Y(p),5,0,7);ctx.fill()})}
if(L.fit&&Math.abs(rho-RHO_FIT)<1e-3){ctx.fillStyle=col.fit;MIDS.forEach(m=>{ctx.beginPath();ctx.arc(X(m),Y(m),3.5,0,7);ctx.fill()})}
ctx.fillStyle=col.bindu;ctx.beginPath();ctx.arc(cx,cy,2.4,0,Math.PI*2);ctx.fill();
document.getElementById('rv').textContent=rho.toFixed(4);
document.getElementById('note').textContent=
document.getElementById('note').textContent=L.coll?'Collapsed yantra (lavender): both rings exact (dashed, r = 0.6127 and 0.4155). Two bases fuse into one line (thick) carrying three 6-fold points: one on the axis, a mirror pair beside it. Many other coincidences appear too; it is a degenerate corner of the family.':
'Yantra in gold and pink; its inversion in the rim (ρ = 1) in violet; its inversion in a circle of radius ρ in teal. '+
(Math.abs(rho-RHO_FIT)<1e-3?`Fit: ρ = ${RHO_FIT.toFixed(4)}. Two big lobes and one inner lobe meet at the image of a triple point (the T4 apex on the T6 base); this ρ puts those meeting points at the radius of the outer upward triangle’s slanted-side midpoints (teal dots), about 0.615 of the rim.`:'Fit trefoil snaps ρ so the lobe meeting points land on the outer triangle’s side midpoints.');
}
const $=id=>document.getElementById(id);
['orig','unit','fit','outer'].forEach(k=>$('l-'+k).onclick=e=>{L[k]=!L[k];e.currentTarget.setAttribute('aria-pressed',L[k]);draw()});
['orig','unit','fit','outer','coll'].forEach(k=>$('l-'+k).onclick=e=>{L[k]=!L[k];e.currentTarget.setAttribute('aria-pressed',L[k]);draw()});
$('rho').oninput=e=>{rho=Math.pow(10,+e.target.value);draw()};
$('snap').onclick=()=>{rho=RHO_FIT;$('rho').value=Math.log10(rho);if(!L.fit){L.fit=true;$('l-fit').setAttribute('aria-pressed','true')}draw()};
$('rho').value=Math.log10(RHO_FIT);