function* animatePlan(plan) {
const size=400;
const nominalArmLength=3;
const pad=10;
const c = DOM.context2d(size, size);
const scale=(size-2*pad)/(4*nominalArmLength);
const xy0 = size/2;
const to=(x,y)=>[xy0+x*scale,xy0-y*scale];
c.lineCap = "round";
c.font = `${Math.round(size/20)}px sanserif`;
c.textAlign = "right";
const twopi = 2*Math.PI;
const r0 = scale*nominalArmLength;
const [x0,y0]=to(0,0);
const period = plan.scheduleT.totalTime;
const t0 = Date.now();
while(true) {
const t = (Date.now()-t0)/1000 % period;
const {theta:[T,xT,yT],phi:[P,xP,yP]} = plan.getPosition(t);
let [x,y] = to(xT,yT);
c.globalAlpha=1, c.fillStyle="#fff", c.fillRect(0, 0, size, size);
c.strokeStyle="#aaa", c.lineWidth=1,
c.beginPath(), c.arc(x0, y0, r0, 0, twopi), c.stroke(),
c.beginPath(), c.arc(x0, y0, 2*r0, 0, twopi), c.stroke();
c.fillStyle="#000", c.textAlign="right", c.fillText(`t=${Number(t).toFixed(1)}s`, size-10, size-10);
c.textAlign="left", c.fillStyle="red", c.fillText(`θ=${Number(T).toFixed(0)}°`, 10, size-10);
c.fillStyle="blue", c.fillText(`ϕ=${Number(P).toFixed(0)}°`, 10, size-size/15-10);
c.strokeStyle="red", c.beginPath(), c.lineWidth = Math.round(0.5*scale),
c.moveTo(x0, y0), c.lineTo(x,y), c.stroke();
c.strokeStyle="blue", c.globalAlpha = 0.8, c.beginPath(), c.moveTo(x,y);
[x,y] = to(xP,yP);
c.lineTo(x,y), c.stroke();
yield c.canvas;
}
}