8

svg では、path-d 属性の「arc 要素」のすべてのパラメーターを返す関数を作成したいと考えています。始点、終点、経由点を指定します。(非直線上の 3 点は円の定義による)。円弧 (rx == ry) だけに興味があります。

中心と半径を簡単に計算できます。しかし、私は2つのフラグに苦労しています.3点のトポロジを比較してこれらのフラグを設定する方法は明確に定義されていますか? 互いの角度や距離など?)

スイープ フラグのフラグ、最小アーク対最大アーク、時計回り対反時計回りの意味を認識しています。

4

2 に答える 2

9

はい、svg パス アーク セグメントの大円弧フラグとスイープ フラグは、そのアークの始点 (S)、経由点 (V) および終点 (E) によって形成される角度を調べることで判断できます。

大きな円弧フラグの場合:

  • |∠SVE|の場合 > π/2 の場合、フラグ = 0
  • |∠SVE|の場合 < π/2 の場合、フラグ = 1
    • V を中心とする角度が鋭角か鈍角かを判断しています。
    • その角度が「尖っている」(つまり鋭角である) 場合は、円を大きく周回することになります。

スイープ フラグの場合:

  • ∠ESV > 0 の場合、フラグ = 0
  • ∠ESV < 0 の場合、フラグ = 1
    • V ポイントが S-E 線のどちら側にあるかを判断しています。
    • S から E に向かって見て、V が右にある場合、円の周りを反時計回りに移動します。

次の点に注意してください。

  • 角度を形成するポイントの順序が重要です。それぞれ、大きなアーク フラグとスイープ フラグの SVE と ESV です。
  • すべての角度は -π から π の間、つまり -180° から 180° の間でなければなりません。
  • ラージ アーク フラグを決定するために絶対値が使用されますが、スイープ フラグは使用されません。

この回答の下部にあるデモ コードは、これらの計算を示しています。OP の質問に直接答えるために重要な唯一のコードは、最初の 2 行です。

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;

デモを使用するには、[コード スニペットの実行] ボタンをクリックし、長方形を 3 回クリックして、開始点 (S)、経由点 (V)、終了点 (E) をこの順序で配置します。デモは半径を計算し、2 つのフラグ ( 、 、 ) のさまざまな組み合わせで可能な 4 つの円弧すべてを0,0描画0,11,0ます1,1。大きな弧と小さな弧はそれぞれ青と赤になり、時計回りと反時計回りの弧はそれぞれ実線と点線になります。1 つの正しい円弧が黄色で強調表示されます。円弧が描画されたら、3 回再クリックして繰り返すことができます。同じ場所を 2 回クリックしたり、直線を 3 回クリックしたりしても、幾何学的に予想されるように、円弧は作成されないことに注意してください。

この回答の最初の投稿 (2016 年 11 月 5 日) の時点で、デモは Chrome、Opera、Safari では機能しますが、Firefox では機能しないことに注意してください。(Explorer や Edge、モバイル ブラウザーは確認していません。)これは、ES6/ES2015 コードを使用したことが原因ではないかと考えています。しかし、コードエディタの「Use 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>

于 2016-11-06T00:20:12.330 に答える
1

私は最近、svg 円弧でいくつかの作業を行い、以下を使用して円弧スイープと d 値を取得しました。これは役に立つかもしれません。

//---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(" ");
于 2014-02-16T22:47:49.593 に答える