【问题标题】:How to find the halfway mark on a bezier curve?如何在贝塞尔曲线上找到中间标记?
【发布时间】:2021-12-19 03:38:53
【问题描述】:

我正在尝试计算点在贝塞尔曲线上的位置,并且我正在使用标准公式来计算。那就是:

x = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * cpX + t * t * x2;
y = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * cpY + t * t * y2;

但是,我对结果感到很困惑。我已经为我在下面谈论的内容开发了一个演示:

let c = document.querySelector("canvas");
let ctx = c.getContext("2d");

let x1 = 25;
let y1 = 25;
let cpX = 35;
let cpY = 35;
let x2 = 200;
let y2 = 25;

let f = 0;

function calcX(t) {
  return (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * cpX + t * t * x2;
}

function calcY(t) {
  return (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * cpY + t * t * y2;
}

function drawCurve() {
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.quadraticCurveTo(cpX, cpY, x2, y2);
  ctx.stroke();
}

function drawLoop(elapsed) {  
  c.width = 600;
  c.height = 600;
  
  let x = calcX(f);
  let y = calcY(f);

  drawCurve();

  ctx.beginPath();
  ctx.rect(x, y, 3, 3);
  ctx.stroke();
  
  f = f < 1 ? f + 0.001 : 0;

  document.querySelector(".debug").innerHTML = f.toFixed(2);

  requestAnimationFrame(drawLoop);
}

drawLoop(0);
.debug { position: absolute; left: 9px; top: 6px; }
<canvas></canvas>
<div class="debug"></div>

如您所见,50% 标记似乎离左边太远了:

我知道有一条曲线,但中途标记似乎仍然离左边太远。也就是说,如果你问一群人,这条曲线的中间标记在哪里,我相信他们都会说向右更远。

这可能是一个长镜头,但是否还有另一个公式可以计算贝塞尔曲线上更“接近真实世界”的点?

编辑:我只是想到了另一种更具体地表达这种现象的方法。您会注意到,如果将 cpXcpY 变量值设置为任意数字,然后运行模拟,方形标记会沿着曲线的不同部分以不同的速度移动。也就是说,它可能在开始时快速移动,然后减速,然后在曲线的末端再次加速。

我正在寻找的是一个公式,这样方形标记将在整个曲线上以恒定速度进行,并且永远不会沿途加速或减速。这可能吗?

【问题讨论】:

  • 在 C# 中复制但带有资源链接:stackoverflow.com/questions/23596802/…
  • 哦,这只对寻找中点有用吗?我知道这是我的标题所指定的,但我想我认为解决方案将是通用的,并允许您找到曲线上的任何点,因为我最终也会需要它。
  • 问题的根本原因是这样的:i.imgur.com/wDvwiQH.png - 给我几分钟,我会想出一个解决方案。
  • @ChrisG 是的,这张照片完全解释了我的意思!我很高兴你能理解我的乱码解释。
  • 这是一个解决方案:jsfiddle.net/s5cp9t01(我将 100 个点的坐标保存在一个数组中,计算点之间的线段长度,然后沿着线段行走直到达到目标长度.

标签: javascript html algorithm bezier spline


【解决方案1】:

我最终根据 Chris G 的评论制作了一个函数,该函数预先计算了贝塞尔曲线上的所有这些点。其他人可能会发现它很有用:

function calcPoints() {
  const step = 0.001;
  const segments = [];

  for (let i = 0; i <= 1 - step; i += step) {
    let dx = this.calcX(i) - this.calcX(i + step);
    let dy = this.calcY(i) - this.calcY(i + step);

    segments.push(Math.sqrt(dx * dx + dy * dy));
  }

  const len = segments.reduce((a, c) => a + c, 0);

  let result = [];
  let l = 0;
  let co = 0;

  for (let i = 0; i < segments.length; i++) {
    l += segments[i];
    co += step;
    result.push({ t: l / len, co });
  }

  return result;
}

// Example pseudo-code:
let pointCache = calcPoints();
let co = binarySearch(pointCache, 0.5).co;
let x = this.calcX(co);
let y = this.calcY(co);

我认为这行得通?您只需对结果数组进行二进制搜索以查找您要查找的值。如果您的曲线之后没有变化,则该函数只需要运行一次。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 2011-03-11
  • 1970-01-01
  • 2012-07-13
  • 2011-03-10
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多