在 svg 中,我想构建一个函数,该函数返回 path-d 属性中“弧元素”的所有参数。给定一个起点、终点和一个通过点。(非直线上的 3 个点是圆上的每个定义)。我只对圆弧(rx == ry)感兴趣。
我可以很容易地计算出中心和半径。但是我在这两个标志上苦苦挣扎,是否有一个明确的定义,如何通过比较 3 个点的拓扑来设置这些标志?像彼此的角度或距离?)
我知道标志的含义,最小与最大的弧,顺时针与逆时针的扫描标志。
在 svg 中,我想构建一个函数,该函数返回 path-d 属性中“弧元素”的所有参数。给定一个起点、终点和一个通过点。(非直线上的 3 个点是圆上的每个定义)。我只对圆弧(rx == ry)感兴趣。
我可以很容易地计算出中心和半径。但是我在这两个标志上苦苦挣扎,是否有一个明确的定义,如何通过比较 3 个点的拓扑来设置这些标志?像彼此的角度或距离?)
我知道标志的含义,最小与最大的弧,顺时针与逆时针的扫描标志。
是的,您可以通过查看由该弧的起点 (S)、通过 (V) 和终点 (E) 点形成的角度来确定 svg 路径弧段的大弧和扫描标志。
对于大圆弧标志:
对于扫旗:
请注意以下事项:
此答案底部的演示代码演示了这些计算。直接回答 OP 问题的唯一重要代码是前两行:
const lgArcFl = (S,V,E) => Math.abs(angle(S,V,E)) > pi/2 ? 0 : 1;
const sweepFl = (S,V,E) => angle(E,S,V) > 0 ? 0 : 1;
要使用演示,请单击“运行代码片段”按钮,然后在矩形上单击 3x 以按顺序定位开始 (S)、通过 (V) 和结束 (E) 点。该演示将计算半径,然后使用两个标志的各种组合(即0,0
、0,1
和)绘制所有可能1,0
的4 条弧线1,1
。大圆弧和小圆弧分别为蓝色和红色,而顺时针和逆时针圆弧分别为实心和虚线。一个正确的弧将以黄色突出显示。绘制弧线后,您可以重新单击 3x 以重复,等等。请注意,在同一位置单击两次或在直线上单击 3x 不会产生任何弧线,这在几何上是预期的。
请注意,在我最初发布此答案时(2016 年 11 月 5 日),该演示适用于 Chrome、Opera 和 Safari,但不适用于 Firefox。(我没有检查过 Explorer 或 Edge,也没有检查过任何移动浏览器。)我怀疑这可能是因为我使用了 ES6/ES2015 代码。但是,由于我不明白的原因,在代码编辑器中单击“使用 BabelJS / ES2015”按钮会以某种方式使代码无法使用。因此,如果您无法让演示正常工作,不妨尝试使用不同的浏览器。
const lgArcFl = (S,V,E) => Math.abs(angle(S,V,E)) > pi/2 ? 0 : 1;
const sweepFl = (S,V,E) => angle(E,S,V) > 0 ? 0 : 1;
const angle = ([a,b],[c,d],[e,f]) => (Math.atan2(f-d,e-c)-Math.atan2(b-d,a-c)+3*pi)%(2*pi)-pi;
const qs = sel => document.querySelector(sel), pi = Math.PI, pts = [];
const radius = ([a,b],[c,d],[e,f]) => {
const g=c-a,h=2*(c-e)/g,i=d-b,j=c*c+d*d,k=j-a*a-b*b,l=(j-e*e-f*f-h*k/2)/(2*(d-f)-h*i);
return Math.hypot(a+(i*l-k/2)/g,b-l);
};
const mkArc = (arc, [sx, sy], [ex, ey], r, lg, sw) => arc.setAttribute('d',
`M ${sx} ${sy} A ${r} ${r} 0 ${lg} ${sw} ${ex} ${ey}`);
const calcArcs = (S,V,E) => {
const args = [S, E, radius(S,V,E)];
[[0,0],[0,1],[1,0],[1,1]].forEach(([lg,sw]) => mkArc(qs(`#arc${lg}${sw}`), ...args, lg, sw));
mkArc(qs(`#arc`), ...args, lgArcFl(S,V,E), sweepFl(S,V,E));
};
let ptNum = 0;
qs('svg').addEventListener('click', evt => {
const x = evt.x - 10, y = evt.y - 10;
pts[ptNum] = [x, y];
qs('#pt' + ptNum).setAttribute('transform', `translate(${x},${y})`);
if (ptNum++ === 2) {
calcArcs(...pts);
ptNum = 0;
}
});
text {
font-family: courier;
font-size: 18px;
fill: black;
stroke: none;
transform: translate(-5px,5px);
}
#arc00, #arc01 {
stroke: red;
}
#arc10, #arc11 {
stroke: blue;
}
#arc00, #arc10 {
stroke-width: 4;
stroke-dasharray: 5,5;
}
#arc01, #arc11 {
stroke-width: 2;
}
rect {
fill: none;
stroke: black;
height: 200px;
width: 600px;
}
<svg height="200" width="600">
<defs>
<path id="arc" />
<circle id="circ" cx="0" cy="0" r="10" />
</defs>
<g fill="none">
<use xlink:href="#arc" stroke="black" stroke-width="12" />
<use xlink:href="#arc" stroke="#ff8" stroke-width="10" />
<path id="arc00" />
<path id="arc01" />
<path id="arc10" />
<path id="arc11" />
</g>
<g fill="#ff8" stroke="black">
<g id="pt0" transform="translate(-99,0)"><use xlink:href="#circ" /><text>S</text></g>
<g id="pt1" transform="translate(-99,0)"><use xlink:href="#circ" /><text>V</text></g>
<g id="pt2" transform="translate(-99,0)"><use xlink:href="#circ" /><text>E</text></g>
</g>
<rect />
</svg>
I recently did some work on svg circular arcs and used the following to get the arc sweep and d values. This may be helpful.
//---x1,y1 and x2,y2 are the two end points---
function polarToCartesian(centerX, centerY,radius, angleInDegrees)
{
var angleInRadians = (angleInDegrees) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
var startAngle = 180/Math.PI*Math.atan2(y1-cy, x1-cx);
var endAngle = 180/Math.PI*Math.atan2(y2-cy, x2-cx);
StartPnt = polarToCartesian(cx, cy, radius, startAngle);
EndPnt = polarToCartesian(cx, cy, radius, endAngle);
ArcSweep = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", StartPnt.x, StartPnt.y,
"A", radius, radius, 0, ArcSweep, 0, EndPnt.x, EndPnt.y
].join(" ");