// from:
// https://github.com/DmitryBaranovskiy/raphael/blob/b5fdbdc9850827eb49622cd0bc81b26025f8128a/dev/raphael.core.js#L1019
// and:
// http://schepers.cc/getting-to-the-point
function catmullRom2bezier(crp, close) { const d = [];
for (let i = 0, iLen = crp.length; iLen - 2 * !close > i; i += 2) { const p = [
{ x: crp[i - 2], y: crp[i - 1] }, { x: crp[i], y: crp[i + 1] }, { x: crp[i + 2], y: crp[i + 3] }, { x: crp[i + 4], y: crp[i + 5] } ];
if (close) { if (!i) { p[0] = { x: crp[iLen - 2], y: crp[iLen - 1] }; } else if (iLen - 4 == i) { p[3] = { x: crp[0], y: crp[1] }; } else if (iLen - 2 == i) { p[2] = { x: crp[0], y: crp[1] }; p[3] = { x: crp[2], y: crp[3] }; }
} else { if (iLen - 4 == i) { p[3] = p[2];
} else if (!i) { p[0] = { x: crp[i], y: crp[i + 1] }; }
}
d.push([
'C',
(-p[0].x + 6 * p[1].x + p[2].x) / 6,
(-p[0].y + 6 * p[1].y + p[2].y) / 6,
(p[1].x + 6 * p[2].x - p[3].x) / 6,
(p[1].y + 6 * p[2].y - p[3].y) / 6,
p[2].x,
p[2].y
]);
}
// really for demo purposes:
// draw a tiny rect if less than 2 points
console.log(d.length);
if (d.length < 2) { const x = crp[0];
const y = crp[1];
return `M ${x} ${y} h 1 v 1 h -1 v-1`;
// draw a line if 2 points
} else if (d.length === 2) { return `M ${crp[0]} ${crp[1]} L ` + crp.flat().join` `; }
// draw curves if 3 or more (raphael only ever did this, other numbers of points
// were considered invalid)
return `M ${crp[0]} ${crp[1]}` + d.flat().join` `;}
// try it out:
const pathCommands = catmullRom2bezier([
100, 100, 200, 100, 200, 300, 170, 150, 120, 180, 120, 120
], true)
const svg = `
click 3 or more times anywhere<br>
<button>clear</button>
<svg>
<path d="M 100 100 ${pathCommands}" stroke="blue" fill="none" /> </svg>
<style>
svg { position:absolute;
left:0;top:0;
overflow:visible;
pointer-events:none;
}
</style>
`;
const div = document.body.appendChild(
document.createElement('div'));
div.innerHTML = svg;
const path = div.querySelector('path')
let pnts = [];
function isButton(e) { if (e.target.tagName === 'BUTTON') { pnts = []
path.setAttribute('d', 'M 0 0L0 0'); return true;
}
}
document.addEventListener('click', e => { if (isButton(e)) return;
pnts.push(e.clientX, e.clientY);
path.setAttribute('d', catmullRom2bezier(pnts, true))});
document.addEventListener('touchend', e => { if (isButton(e)) return;
pnts.push(e.touchs[0].clientX, e.touchs[0].e.clientY);
path.setAttribute('d', catmullRom2bezier(pnts, true))});