我试图将SVG elliptical arc path commands转换为中心点,开始/停止角度,以及可能的缩放/旋转(在省略号的情况下)。
我按照 Conversion from Endpoint to Center Parameterization 的SVG实施说明中的公式,但大多数情况下我没有得到好的值。请参阅下面的代码段,其中更改输入值将以红色绘制SVG弧,然后尝试以绿色在HTML5画布上绘制相同的弧。
我很确定我还需要校正超出范围的半径,而且我知道我还没有处理椭圆(而不是圆圈),但即使是半径也是同样正确,我的代码返回错误的开始/结束值。
任何人都可以从我在SVG规范中列出的公式转换中看到错误,或者方程本身出错吗?
const { mat2, vec2 } = this.vmath || require('vmath');
[].forEach.call(
document.querySelectorAll('#x1in,#y1in,#x2in,#y2in,#ƒain,#ƒsin,#rxin,#ryin,#φin'),
inp => { inp.addEventListener('input',recalc,false) }
);
recalc();
function recalc() {
const x1=x1in.value*1, y1=y1in.value*1,
x2=x2in.value*1, y2=y2in.value*1,
ƒa=ƒain.value*1, ƒs=ƒsin.value*1,
rx=rxin.value*1, ry=ryin.value*1,
φ=φin.value*1;
path.setAttribute('d',['M',x1,y1,'A',rx,ry,φ,ƒa,ƒs,x2,y2].join(' '));
// F.6.5.1
const cosφ = Math.cos(φ*Math.PI/180),
sinφ = Math.sin(φ*Math.PI/180);
const tmpM2 = mat2.new(cosφ, -sinφ, sinφ, cosφ);
const cp = vec2.new((x1-x2)/2, (y1-y2)/2);
vec2.transformMat2(cp,cp,tmpM2);
const x1p=cp.x, y1p=cp.y;
x1po.innerHTML = x1p;
y1po.innerHTML = y1p;
// F.6.5.2
vec2.set(cp, rx*y1p/ry, -ry*x1p/rx);
const rx2=rx*rx, ry2=ry*ry, x1p2=x1p*x1p, y1p2=y1p*y1p;
vec2.scale(cp, cp, (ƒa==ƒs?-1:1) * Math.sqrt(
(rx2*ry2 - rx2*y1p2 - ry2*x1p2) /
(rx2*y1p2 + ry2*x1p2)
));
cpxo.innerHTML = cp.x;
cpyo.innerHTML = cp.y;
// F.6.5.3
mat2.set(tmpM2, cosφ, sinφ, -sinφ, cosφ);
const c = vec2.transformMat2(vec2.new(),cp,tmpM2);
c.x += (x1+x2)/2;
c.y += (y1+y2)/2;
cxo.innerHTML = c.x;
cyo.innerHTML = c.y;
const v1 = vec2.new(( x1p-cp.x)/rx, ( y1p-cp.y)/ry);
const v2 = vec2.new((-x1p-cp.x)/rx, (-y1p-cp.y)/ry);
// F.6.5.5
const θ1 = angleBetween( vec2.new(1,0), v1 );
θ1o.innerHTML = θ1*180/Math.PI;
// F.6.5.6
const Δθ = angleBetween( v1, v2 ) % (Math.PI*2);
Δθo.innerHTML = Δθ*180/Math.PI;
let ctx=can.getContext('2d');
// TODO: φ rotation(?) and scaling if rx!=ry
ctx.clearRect(0,0,400,200);
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(0,255,0,1)';
ctx.beginPath();
ctx.arc(c.x, c.y, rx, θ1, θ1-Δθ, false);
ctx.stroke();
}
// F.6.5.4
function angleBetween(u,v) {
const sign = (u.x*v.y - u.y*v.x > 1) ? 1 : -1;
return sign * Math.acos( vec2.dot(u,v)/vec2.length(u)/vec2.length(v) );
}

svg, canvas { position:absolute; top:0; left:0; width:400px; height:200px }
svg path { fill:none; stroke:red; stroke-width:4px }
#ui { margin-top:200px }
div { margin-bottom:0.5em }
label { margin-right:1em; white-space:nowrap }
input { width:2.5em; text-align:right }
span { font-family:sans-serif }
img { display:block; margin:2em }

<script src="https://cdn.rawgit.com/gamedev-js/vmath/1.3.1/dist/vmath.dev.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 200">
<path id="path" d="M0,0 L200,200"/>
</svg>
<canvas id="can" width="400" height="200"></canvas>
<div id="ui">
<div>
<label>x1: <input id="x1in" value="30" type="number" step="1"></label>
<label>y1: <input id="y1in" value="30" type="number" step="1"></label>
<label>x2: <input id="x2in" value="47" type="number" step="1"></label>
<label>y2: <input id="y2in" value="47" type="number" step="1"></label>
<label>ƒa: <input id="ƒain" value="1" type="number" step="1" min="0" max="1"></label>
<label>ƒs: <input id="ƒsin" value="1" type="number" step="1" min="0" max="1"></label>
<label>rx: <input id="rxin" value="17" type="number" step="1"></label>
<label>ry: <input id="ryin" value="17" type="number" step="1"></label>
<label>φ: <input id="φin" value="0" type="number" step="1"></label>
</div>
<div>
<label>x<sub>1</sub>ʹ: <span id="x1po"></span></label>
<label>y<sub>1</sub>ʹ: <span id="y1po"></span></label>
<label>c<sub>x</sub>ʹ: <span id="cpxo"></span></label>
<label>c<sub>y</sub>ʹ: <span id="cpyo"></span></label>
</div>
<div>
<label>c<sub>x</sub>: <span id="cxo"></span></label>
<label>c<sub>y</sub>: <span id="cyo"></span></label>
<label>θ<sub>1</sub>: <span id="θ1o"></span></label>
<label>∆θ: <span id="Δθo"></span></label>
</div>
&#13;