JSFiddle - React, Tailwind, and code Playground
by Simon Hi
HTML
<h1>Cubic Bézier curve + middle point</h1>
<svg id="svgRef" width="250" height="250">
<path id="pRef" d="M 75,75 c 20,180 180,20 -5,-5" fill="transparent" stroke="blue" stroke-width="10" />
<circle id="cRef" cx="10" cy="10" r="10" fill="transparent" stroke="blue" stroke-width="10" />
</svg>
<br>
<div class="control">
<span>M :</span>
<input id="mx" type="text" onkeyup="updateSvg()">
<input id="my" type="text" onkeyup="updateSvg()"><br>
<span>c1 :</span>
<input id="c1x" type="text" onkeyup="updateSvg()">
<input id="c1y" type="text" onkeyup="updateSvg()"><br>
<span>c2 :</span>
<input id="c2x" type="text" onkeyup="updateSvg()">
<input id="c2y" type="text" onkeyup="updateSvg()"><br>
<span>c3 :</span>
<input id="c3x" type="text" onkeyup="updateSvg()">
<input id="c3y" type="text" onkeyup="updateSvg()"><br>
</div>
<div id="trace"></div>
CSS
body {text-align:center;}
svg {border:10px solid blue;}
#control span {display:inline-block;width:2em;text-align:right;margin-right:0.5em;}
#trace {display:table;margin:1em auto;text-align:left;}
#trace span:nth-of-type(2),
#trace span:nth-of-type(3) {padding-left:2em;}
JavaScript
// compute cubic Bezier curves middle
function computePt(t,c)
{
var pt={x:0,y:0};
var t1,t1_3,t1_3a,t1_3b,t1_3c;
t1=1.0-t;
t1_3=t1*t1*t1;
t1_3a=(3*t)*(t1*t1);
t1_3b=(3*(t*t))*t1;
t1_3c=(t*t*t);
pt.x=(c[0].x*t1_3)+(t1_3a*c[1].x)+(t1_3b*c[2].x)+(t1_3c*c[3].x);
pt.y=(c[0].y*t1_3)+(t1_3a*c[1].y)+(t1_3b*c[2].y)+(t1_3c*c[3].y);
return pt;
}
function cubicBezierCurvesMiddle(m,c)
{
var k,kmax=1000;
var len=0.0,len2=0.0;
var t=0;
var pt,exPt={x:0,y:0};
var x,y;
for (k=0;k<=kmax;k++)
{
pt=computePt(k/kmax,c);
if (k>0)
{
x=pt.x-exPt.x;
y=pt.y-exPt.y;
len=len+Math.sqrt(x*x+y*y);
exPt.x=pt.x;
exPt.y=pt.y;
}
}
exPt={x:0,y:0};
for (k=0;k<=kmax;k++)
{
pt=computePt(k/kmax,c);
if (k>0)
{
x=pt.x-exPt.x;
y=pt.y-exPt.y;
len2=len2+Math.sqrt(x*x+y*y);
exPt.x=pt.x;
exPt.y=pt.y;
}
if (len2>=len/2) break;
}
return {x:Math.round((pt.x+m.x)*100)/100,y:Math.round((pt.y+m.y)*100)/100};
}
// manage the test form
function getVal(id)
{
var v=document.getElementById(id).value;
if (!v) return 0;
if (isNaN(v)) return 0;
return parseInt(v);
}
function updateSvg()
{
var m,c=[],pt,s,t;
m={x:getVal("mx"),y:getVal("my")};
c[0]={x:0,y:0};
c[1]={x:getVal("c1x"),y:getVal("c1y")};
c[2]={x:getVal("c2x"),y:getVal("c2y")};
c[3]={x:getVal("c3x"),y:getVal("c3y")};
s="M "+m.x+","+m.y+" c "+c[1].x+","+c[1].y+" "+c[2].x+","+c[2].y+" "+c[3].x+","+c[3].y;
document.getElementById("pRef").setAttribute("d",s);
pt=cubicBezierCurvesMiddle(m,c);
document.getElementById("cRef").setAttribute("cx",pt.x);
document.getElementById("cRef").setAttribute("cy",pt.y);
t="<span><svg width=\"250\" height=\"250\"></span>";
t+="<br>";
t+="<span><path d=\""+s+"\" fill=\"transparent\" stroke=\"blue\" stroke-width=\"10\" /></span>";
t+="<br>";
t+="<span><circle cx=\""+pt.x+"\" cy=\""+pt.y+"\" r=\"10\" fill=\"transparent\" stroke=\"blue\" stroke-width=\"10\"...