svg arc,如何确定给定起点和终点的扫掠和弧度标记

dr *_*rry 5 geometry svg geometric-arc

在svg中,我想构建一个函数,该函数在path-d属性中返回“弧形元素”的所有参数。给定起点,终点和通孔。(非直线上的3个点是圆上每个定义的点)。我只对圆弧(rx == ry)感兴趣。

我可以很容易地计算出中心和半径。但是我在2个标记中苦苦挣扎,是否有一个清晰的定义如何通过比较3个点的拓扑来设置这些标记?像彼此之间的角度或距离?)

我知道标志的含义,即扫描标志的最小弧与最大弧,顺时针与逆时针。

And*_*ems 5

是的,您可以通过查看弧的起点(S),通过(V)和终点(E)形成的角度来确定svg路径弧段的大弧和扫掠标志。

对于大弧形标志:

  • 如果|∠SVE| >π/ 2,则标志= 0
  • 如果|∠SVE| <π/ 2,则标志= 1
    • 您正在确定以V为中心的角度是锐角还是钝角。
    • 如果那个角度是“尖的”(即锐角),那么您将绕圆弧走很长一段路。

对于扫描标志:

  • 如果∠ESV> 0,则标志= 0
  • 如果∠ESV<0,则标志= 1
    • 您正在确定V点位于S-to-E线的哪一侧。
    • 当您从S朝E观看时,如果V在右侧,则您将沿圆圈逆时针移动。

请注意以下几点:

  • 构成角度的点的顺序很重要:分别是大弧标记和扫掠标记的SVE与ESV。
  • 所有角度都必须在-π和π之间,即-180°和180°之间。
  • 绝对值用于确定大弧标志,而不是扫掠标志。

此答案底部的演示代码演示了这些计算。对于直接回答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;
Run Code Online (Sandbox Code Playgroud)

要使用该演示,请单击“运行代码段”按钮,然后在矩形上单击3x,以通过(V)和结束(E)点按此顺序定位起点(S)。该演示将计算半径,然后使用两个标记0,00,11,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;
Run Code Online (Sandbox Code Playgroud)
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;
  }
});
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)