1

質問

キュービック (2d) ベジェ曲線B(t)の点Q を取得する必要があります。ここで、点Qから別の特定の点Pへの線がベジェ曲線と垂直に交差します。

  • 私は知っています: P , B(t)
  • Qを探します(基本的には g の勾配が必要ですが、 Qがわかっていれば簡単に計算できますが、 gの勾配で十分です)


今までやってきたこと(飛ばしていいです)

このアンザッツは間違っていると思うことに注意してください。これは完全を期すためにのみ含まれています。

数学の(基本的な)知識でこれを解こうとしましたが、完成できません。これは私が今持っているものです(表記法に厳密になりすぎないでください。私はこれがあまり得意ではありません):

次の式は、 y(x)x(y)について計算する必要がある 1 つの結果を取得するためにy(x)として表されます。点Pは制御点、QはQからPへの線g(x)がベジエ曲線B (t) = (x, y) Tで垂直になる点です。線g(x)の式は、次のように取得できます。

ここで、B(x)はデカルト座標のベジエ曲線、B'(x)は微分 (デカルト座標)、kは y 軸との交点です。g(x)の勾配を得るには、解かなければなりません

B(x)を計算するには、 B (t)tについて解いてから、それをB (t) に代入する必要があります。したがって、ベジエ曲線上の各点には関係があります

これは導関数B '(t)にも当てはまります。

B (t)の導関数は (ウィキペディアによると)

これを t について ( wolfram alphaで) 解くと、

ここで、a 0 = ( P 1 - P 0 ) xa 1 = ( P 2 - P 1 ) x、およびa 2 = ( P 3 - P 2 ) xです。*t i *s をB (t)に戻すと、 ( t 1wolfram alpha 、t 2wolfram alpha 、t 3の wolfram alpha )が得られます。

次に、y = B'(x)と 2 番目の方程式を使用してxを消去しますが、方法がわかりませんし、これが可能かどうかもわかりません。

4

2 に答える 2

0

ベジェ曲線について@Mike'Pomax'Kamermans によって実際に作成されたこのオンラインブックのアイデアを実装した Github の mbostockによって作成された私の問題の近似の実装を見つけました。ベジエ曲線に対処する必要がある場合は、これを確認してください。これは、私がベジエ曲線で抱えていた問題のほとんどを説明しています。

アルゴリズムのアイデアは次のとおりです。

  1. 大まかな概算を行います。
    1. B ( t )に異なるt iを代入してQ approx, iを計算します(mbostock ではt 1 = 0、t 2 = 1/8、t 3 = 2/8、... を使用します)。
    2. Q approx, iPの間の 2 次 (平方根の計算を 1 つ節約) 距離を計算します。
    3. Q approx, iと、最も近い (距離が最小の) t iを保存します。
  2. より正確な近似を行う:
    1. 精度qを選択します。
    2. t before = t i - qを計算します。
    3. Q approx, before = B ( t before ) とPの間の距離がQ approx, iPの間の距離よりも小さいかどうかを確認します。はいの場合、Q approx, i = Q approx, beforeを設定し、2 から開始します (正確な近似)、ない場合は続行します。
    4. t after = t i + qを計算します。
    5. Q approx, after = B ( t after ) とPの間の距離がQ approx, iPの間の距離よりも小さいかどうかを確認します。はいの場合、Q approx, i = Q afterを設定し、2 から開始します (正確な近似)。いいえの場合は続行します。
    6. Q近似、iが最も近い。精度が十分であれば、ここで終了します。精度qを下げない場合(mbostock は 2 で割ります)、2 からやり直します (正確な近似値)。

既に述べたように、Github には mbostockの実装があります。リンクが切れた場合に備えて、ここにコードを貼り付けます。これは私自身のコードではありません。

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);
// adding coordinates display
var coords = svg.append("text");
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]);
  coords.attr("x", (p[0] + m[0]) / 2).attr("y", (p[1] + m[1]) / 2).html("Q(" + Math.round(p[0]) + ", " + Math.round(p[1]) + ")");
}
function closestPoint(pathNode, point) {
  var pathLength = pathNode.getTotalLength(),
      precision = 8,
      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 /= 2;
  while (precision > 0.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 /= 2;
    }
  }
  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;
  }
}
.disclaimer{
  padding: 10px;
  border-left: 3px solid #ffcc00;
  background: #fffddd;
}
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.5.17/d3.min.js"></script>
<div class="disclaimer">
  This is not my own code. This is made by <a href="https://gist.github.com/mbostock/8027637">mbostock on Github</a> based on Mike Kamermans <a href="https://pomax.github.io/bezierinfo/#projections">Primer on Bézier Curves online book</a>.
</div>

于 2020-01-13T10:49:19.333 に答える