9

(a)固定点とd3.jsのsvg:path要素の間の最短距離を計算し、(b)この距離に属するパス上の点を決定する(効率的な)方法はありますか?

4

3 に答える 3

3

一般的な場合、私はそうは思いません。SVGパスは複雑な要素です。たとえば、パスがベジェ曲線の場合、コントロールポイントは表された線から外れ、表された形状はコントロールポイントの境界ボックスから外れている可能性があります。

パスを生成するために使用するポイントのセットがある場合、このポイントを使用して、このポイントから特定のポイントまでの距離を計算し、最小距離を取得できると思います。MDN SVGパスチュートリアルでは、複雑な形状の例とその作成方法を見つけることができます。

于 2013-06-18T21:24:10.543 に答える
2

私の微積分の答えはまだ有効ですが、このbl.ocksの例ですべてを行うことができます:

var points = [[474,276],[586,393],[378,388],[338,323],[341,138],[547,252],[589,148],[346,227],[365,108],[562,62]];

var width = 960,
    height = 500;

var line = d3.svg.line()
    .interpolate("cardinal");

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var path = svg.append("path")
    .datum(points)
    .attr("d", line);

var line = svg.append("line");

var circle = svg.append("circle")
    .attr("cx", -10)
    .attr("cy", -10)
    .attr("r", 3.5);

svg.append("rect")
    .attr("width", width)
    .attr("height", height)
    .on("mousemove", mousemoved);

function mousemoved() {
  var m = d3.mouse(this),
      p = closestPoint(path.node(), m);
  line.attr("x1", p[0]).attr("y1", p[1]).attr("x2", m[0]).attr("y2", m[1]);
  circle.attr("cx", p[0]).attr("cy", p[1]);
}

function closestPoint(pathNode, point) {
  var pathLength = pathNode.getTotalLength(),
      precision = pathLength / pathNode.pathSegList.numberOfItems * .125,
      best,
      bestLength,
      bestDistance = Infinity;

  // linear scan for coarse approximation
  for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
    if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
      best = scan, bestLength = scanLength, bestDistance = scanDistance;
    }
  }

  // binary search for precise estimate
  precision *= .5;
  while (precision > .5) {
    var before,
        after,
        beforeLength,
        afterLength,
        beforeDistance,
        afterDistance;
    if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
      best = before, bestLength = beforeLength, bestDistance = beforeDistance;
    } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
      best = after, bestLength = afterLength, bestDistance = afterDistance;
    } else {
      precision *= .5;
    }
  }

  best = [best.x, best.y];
  best.distance = Math.sqrt(bestDistance);
  return best;

  function distance2(p) {
    var dx = p.x - point[0],
        dy = p.y - point[1];
    return dx * dx + dy * dy;
  }
}
path {
  fill: none;
  stroke: #000;
  stroke-width: 1.5px;
}

line {
  fill: none;
  stroke: red;
  stroke-width: 1.5px;
}

circle {
  fill: red;
}

rect {
  fill: none;
  cursor: crosshair;
  pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

そして、私はその間ずっと前の答えでかなりのLaTeXを書いていました!

于 2015-11-25T05:05:12.273 に答える
1

私はこれに対するd3固有の解決策を知りません。しかし、パスを関数のセグメントとして表すことができる場合は、少し微積分で希望があります。

  1. 線の長さの式から始め長さの方程式ます。
  2. ポイントをx1とy1に接続します。
  3. 残りのyを、パスを表す関数に置き換えます。
  4. 単純化してから、導関数を計算しdd / dxます。
  5. 0に設定dd / dxして解決します。うまくいけば、1つのx値がパスエンドポイントの範囲内になります。このxをパス関数に適用すると、パス上にポイントがあります。

より視覚的な例。JavaScriptでこれを実現するための考慮事項はたくさんあります:私の関数は何ですか?上記の派生物を取るための最速の方法は何ですか?これらはあなたの状況に固有のものです。

于 2015-11-25T04:55:57.823 に答える