{
const W = 420, H = 380, CX = 190, CY = 185, SCALE = 65;
const svgNS = "http://www.w3.org/2000/svg";
const pathSelector2 = Inputs.select([
"Circle around origin (n=1)",
"Circle not around origin (n=0)",
"Two loops around origin (n=2)"
], {value: "Circle around origin (n=1)", label: "Path"});
const tSlider2 = Inputs.range([0, 1], {value: 0, step: 0.005, label: "Progress t"});
const pathDefs = {
"Circle around origin (n=1)": {
pts: (t) => [1.5*Math.cos(2*Math.PI*t), 1.5*Math.sin(2*Math.PI*t)],
nWinding: 1
},
"Circle not around origin (n=0)": {
pts: (t) => [1.8 + 0.8*Math.cos(2*Math.PI*t), 0.8*Math.sin(2*Math.PI*t)],
nWinding: 0
},
"Two loops around origin (n=2)": {
pts: (t) => [1.5*Math.cos(4*Math.PI*t), 1.5*Math.sin(4*Math.PI*t)],
nWinding: 2
}
};
function getPathPoint(def, t) { return def.pts(t); }
function computeIntegral(def, upToT, STEPS) {
// Numerically integrate dz/z from 0 to upToT
// Result is a complex number (Re, Im)
let sumRe = 0, sumIm = 0;
const steps = Math.round(upToT * STEPS);
const dt = upToT / Math.max(steps, 1);
for (let s=0; s<steps; s++) {
const t = s / STEPS;
const tn = (s+1) / STEPS;
const [re1, im1] = def.pts(t);
const [re2, im2] = def.pts(tn < 1 ? tn : 1);
// midpoint
const mre = (re1+re2)/2, mim = (im1+im2)/2;
const dre = re2-re1, dim = im2-im1;
const dz_over_z_re = (mre*dre + mim*dim)/(mre*mre+mim*mim);
const dz_over_z_im = (mim*dre - mre*dim)/(mre*mre+mim*mim);
sumRe += dz_over_z_re;
sumIm += dz_over_z_im;
}
return [sumRe, sumIm];
}
function toScreen(re, im) { return [CX + re*SCALE, CY - im*SCALE]; }
function render2(pathName, tVal) {
const def = pathDefs[pathName];
const STEPS = 400;
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", W); svg.setAttribute("height", H);
svg.style.cssText = "background:#fafafa; border:1px solid #e5e7eb; border-radius:6px; display:block;";
// Grid + axes
const gridG = document.createElementNS(svgNS,"g");
gridG.setAttribute("stroke","#e5e7eb"); gridG.setAttribute("stroke-width","0.5");
for (let i=-3; i<=3; i++) {
const xl=document.createElementNS(svgNS,"line"); const [lx]=toScreen(i,0); const [,ly1]=toScreen(0,-2.6); const [,ly2]=toScreen(0,2.6);
xl.setAttribute("x1",lx); xl.setAttribute("y1",ly1); xl.setAttribute("x2",lx); xl.setAttribute("y2",ly2); gridG.appendChild(xl);
const yl=document.createElementNS(svgNS,"line"); const [lx1]=toScreen(-3,0); const [lx2]=toScreen(3,0); const [,ly]=toScreen(0,i);
yl.setAttribute("x1",lx1); yl.setAttribute("y1",ly); yl.setAttribute("x2",lx2); yl.setAttribute("y2",ly); gridG.appendChild(yl);
}
svg.appendChild(gridG);
const [ox,oy]=toScreen(0,0);
const axG=document.createElementNS(svgNS,"g"); axG.setAttribute("stroke","#9ca3af"); axG.setAttribute("stroke-width","1");
const xa=document.createElementNS(svgNS,"line"); const [ax1]=toScreen(-2.8,0); const [ax2]=toScreen(2.8,0);
xa.setAttribute("x1",ax1); xa.setAttribute("y1",oy); xa.setAttribute("x2",ax2); xa.setAttribute("y2",oy);
const ya=document.createElementNS(svgNS,"line"); const [,ay1]=toScreen(0,-2.5); const [,ay2]=toScreen(0,2.5);
ya.setAttribute("x1",ox); ya.setAttribute("y1",ay1); ya.setAttribute("x2",ox); ya.setAttribute("y2",ay2);
axG.appendChild(xa); axG.appendChild(ya); svg.appendChild(axG);
// Full path outline
let pathD = "";
for (let s=0; s<=STEPS; s++) {
const t = s/STEPS;
const [re,im] = def.pts(t);
const [px,py] = toScreen(re,im);
pathD += (s===0 ? `M ${px} ${py}` : ` L ${px} ${py}`);
}
const pathOutline = document.createElementNS(svgNS,"path");
pathOutline.setAttribute("d",pathD); pathOutline.setAttribute("fill","none");
pathOutline.setAttribute("stroke","rgba(249,115,22,0.25)"); pathOutline.setAttribute("stroke-width","2");
svg.appendChild(pathOutline);
// Traced portion
let traceD = "";
const traceSteps = Math.round(tVal * STEPS);
for (let s=0; s<=traceSteps; s++) {
const t = s/STEPS;
const [re,im] = def.pts(t);
const [px,py] = toScreen(re,im);
traceD += (s===0 ? `M ${px} ${py}` : ` L ${px} ${py}`);
}
const traceEl = document.createElementNS(svgNS,"path");
traceEl.setAttribute("d",traceD); traceEl.setAttribute("fill","none");
traceEl.setAttribute("stroke","#f97316"); traceEl.setAttribute("stroke-width","2.5");
svg.appendChild(traceEl);
// Current point
const [cre,cim] = def.pts(tVal);
const [cpx,cpy] = toScreen(cre,cim);
const ptEl = document.createElementNS(svgNS,"circle");
ptEl.setAttribute("cx",cpx); ptEl.setAttribute("cy",cpy); ptEl.setAttribute("r","6");
ptEl.setAttribute("fill","#3b82f6"); ptEl.setAttribute("stroke","#fff"); ptEl.setAttribute("stroke-width","2");
svg.appendChild(ptEl);
// Origin × mark (the singularity 1/z)
const xSize = 5;
const x1m=document.createElementNS(svgNS,"line"); x1m.setAttribute("x1",ox-xSize); x1m.setAttribute("y1",oy-xSize);
x1m.setAttribute("x2",ox+xSize); x1m.setAttribute("y2",oy+xSize);
x1m.setAttribute("stroke","#ef4444"); x1m.setAttribute("stroke-width","2.5"); svg.appendChild(x1m);
const x2m=document.createElementNS(svgNS,"line"); x2m.setAttribute("x1",ox+xSize); x2m.setAttribute("y1",oy-xSize);
x2m.setAttribute("x2",ox-xSize); x2m.setAttribute("y2",oy+xSize);
x2m.setAttribute("stroke","#ef4444"); x2m.setAttribute("stroke-width","2.5"); svg.appendChild(x2m);
const origLbl=document.createElementNS(svgNS,"text"); origLbl.setAttribute("x",ox+8); origLbl.setAttribute("y",oy-6);
origLbl.setAttribute("fill","#ef4444"); origLbl.setAttribute("font-size","10"); origLbl.setAttribute("font-family","sans-serif");
origLbl.textContent="singularity of 1/z"; svg.appendChild(origLbl);
// Winding box
const windBox = document.createElementNS(svgNS,"rect"); windBox.setAttribute("x",W-92); windBox.setAttribute("y",5);
windBox.setAttribute("width",85); windBox.setAttribute("height",44); windBox.setAttribute("fill","#1e40af"); windBox.setAttribute("rx","5");
svg.appendChild(windBox);
const wl=document.createElementNS(svgNS,"text"); wl.setAttribute("x",W-49); wl.setAttribute("y",22);
wl.setAttribute("fill","white"); wl.setAttribute("font-size","10"); wl.setAttribute("text-anchor","middle"); wl.setAttribute("font-family","sans-serif");
wl.textContent="winding n"; svg.appendChild(wl);
const wn=document.createElementNS(svgNS,"text"); wn.setAttribute("x",W-49); wn.setAttribute("y",41);
wn.setAttribute("fill","white"); wn.setAttribute("font-size","18"); wn.setAttribute("text-anchor","middle");
wn.setAttribute("font-family","sans-serif"); wn.setAttribute("font-weight","bold");
wn.textContent=(tVal>0.98)?def.nWinding.toString():"..."; svg.appendChild(wn);
// Integral running display
const [intRe, intIm] = computeIntegral(def, tVal, STEPS);
const finalIntRe = 0;
const finalIntIm = 2*Math.PI*def.nWinding;
const intDiv = document.createElement("div");
intDiv.style.cssText = "margin-top:0.5rem; font-family:sans-serif; font-size:0.88em; padding:0.5rem; background:#f0f9ff; border-radius:4px;";
const intStr = tVal > 0.98
? `∮ dz/z = ${finalIntIm > 0 ? "+" : ""}${finalIntIm.toFixed(2)}i = 2\u03c0i × ${def.nWinding}`
: `Running: ${intRe.toFixed(3)} + ${intIm.toFixed(3)}i`;
intDiv.innerHTML = `<strong>Contour integral:</strong> <span style="color:#1e40af">${intStr}</span>`;
const wrap = document.createElement("div");
wrap.appendChild(svg);
wrap.appendChild(intDiv);
return wrap;
}
const container = document.createElement("div");
container.style.cssText = "border:1px solid #e5e7eb; border-radius:8px; padding:1rem; margin:1rem 0;";
const title = document.createElement("div");
title.style.cssText = "font-weight:600; margin-bottom:0.5rem; font-family:sans-serif;";
title.textContent = "Winding number = contour integral: \u222e dz/z = 2\u03c0i \xd7 n";
container.appendChild(title);
container.appendChild(pathSelector2);
container.appendChild(tSlider2);
const vizDiv2 = document.createElement("div");
container.appendChild(vizDiv2);
const note = document.createElement("div");
note.style.cssText = "margin-top:0.5rem; font-size:0.82em; color:#6b7280; font-style:italic;";
note.textContent = "Move the slider to trace the path. When the loop closes: \u222e dz/z = 2\u03c0i times the winding number. The red \u00d7 marks the singularity at the origin. This is the seed from which the residue theorem grows.";
container.appendChild(note);
function update2() {
vizDiv2.innerHTML = "";
vizDiv2.appendChild(render2(pathSelector2.value, tSlider2.value));
}
pathSelector2.addEventListener("input", update2);
tSlider2.addEventListener("input", update2);
update2();
return container;
}