美文网首页
js:使用规划算法思想生成平滑贝塞尔曲线

js:使用规划算法思想生成平滑贝塞尔曲线

作者: 时间煮鱼 | 来源:发表于2023-12-17 17:13 被阅读0次

最终效果路线(红色线)

ps: 这样做出来的,并不一定是最优的,姑且能用吧

1702890718753.png

先说问题:

当前有:两条带方向的直线和一条贝塞尔曲线,贝塞尔曲线的两端连接两条直线的各一端;
那么:有一辆车沿着这组成的线段行驶,如何将贝塞尔修改成最优的曲线使车最少进行原地旋转

解法思虑:

1、找到两条带方向直线的方向(航向)角度
2、沿起点线方向延长出n(我这边是12)个点&沿终止线反方向延长出n个点

1702889942576.png
将这12 * 12个点,两两组合再加上起点线的末端和终止线的首端生成144条贝塞尔:(这12 * 12是两个控制点)
3、将这144条贝塞尔,每一条切分m(我这边是100)份,计算出每一份的曲率,然后计算这m个曲率的方差
4、找到方差最小的一条就是最平滑的贝塞尔

部分核心代码

// 计算平滑的贝塞尔曲线
export function getSmoothBezier(startPoint, startAngle, endPoint, endAngle) {
  const len = Math.sqrt((startPoint.x - endPoint.x) ** 2 + (startPoint.y - endPoint.y) ** 2);
  const segs = 12; // 将起点和终点之间的距离分成12段
  const step = parseInt(len / segs);
  const allBeziers = []; // 存放所有生成的贝塞尔曲线
  // 起始线段&终点线段延长
  for (let i = 0; i < segs; i++) {
    for (let j = 0; j < segs; j++) {
      allBeziers.push({
        startPoint: { ...startPoint },
        startControlPoint: {
          x: startPoint.x + step * Math.cos(startAngle) * i,
          y: startPoint.y + step * Math.sin(startAngle) * i
        },
        endControlPoint: {
          x: endPoint.x - step * Math.cos(endAngle) * j,
          y: endPoint.y - step * Math.sin(endAngle) * j
        },
        endPoint: { ...endPoint }
      });
    }
  }
  const minBezier = getMinCurvatureBezier(allBeziers);
  return minBezier;
}

// 计算最小曲率的贝塞尔
export function getMinCurvatureBezier(allBeziers) {
  let minBezier = calculateCurvature(allBeziers[0]);
  let minBezierIndex = allBeziers[0];
  allBeziers.forEach(bezier => {
    const curvature = calculateCurvature(bezier);
    if (curvature < minBezier) {
      minBezierIndex = bezier;
      minBezier = curvature;
    }
  });
  return minBezierIndex;
}

// 贝塞尔曲线计算函数
function bezier(t, p0, p1, p2, p3) {
  const u = 1 - t;
  const tt = t * t;
  const uu = u * u;
  const uuu = uu * u;
  const ttt = tt * t;

  const p = {};
  p.x = uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x;
  p.y = uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y;

  return p;
}

// 计算贝塞尔曲线的切线向量
function tangentVector(t, p0, p1, p2, p3) {
  const dt = 0.001; // 微小的增量
  const p1n = bezier(t - dt, p0, p1, p2, p3);
  const p1p = bezier(t + dt, p0, p1, p2, p3);

  // 切线向量
  const tangent = {
    x: p1p.x - p1n.x,
    y: p1p.y - p1n.y
  };

  return tangent;
}

// 计算曲率
function curvature(t, p0, p1, p2, p3) {
  const tangent = tangentVector(t, p0, p1, p2, p3);
  const speed = Math.sqrt(tangent.x ** 2 + tangent.y ** 2);

  // 曲率公式
  const curvature = speed / Math.pow((1 + tangent.x ** 2 + tangent.y ** 2), 1.5);

  return curvature;
}

// 将贝塞尔曲线平分为100段并计算曲率
function calculateCurvature(bezier) {
  const { startPoint, startControlPoint, endControlPoint, endPoint } = bezier;
  const numOfSegments = 100;
  const result = [];

  for (let i = 0; i < numOfSegments; i++) {
    const t = i / numOfSegments;
    const curv = curvature(t, startPoint, startControlPoint, endControlPoint, endPoint);
    result.push(curv);
  }

  // 计算方差
  const variance = calculateVariance(result);
  return variance;
}

// 计算方差
function calculateVariance(result) {
  let sum = 0;
  let sumSquare = 0;
  result.forEach(v => {
    sum += v;
    sumSquare += v ** 2;
  });
  const mean = sum / result.length;
  const variance = sumSquare / result.length - mean ** 2;
  return variance;
}

代码引用

// 计算最优贝塞尔曲线的控制点
export function getOptimalBezier(startLine, endLine) {
  const startLineStartPoint = convertPointInfo(startLine.startPointPosition);
  const startLineEndPoint = convertPointInfo(startLine.endPointPosition);
  const endLineStartPoint = convertPointInfo(endLine.startPointPosition);
  const endLineEndPoint = convertPointInfo(endLine.endPointPosition);

  let startLineAngle = Math.atan2(startLineEndPoint.y - startLineStartPoint.y, startLineEndPoint.x - startLineStartPoint.x);
  let endLineAngle = Math.atan2(endLineEndPoint.y - endLineStartPoint.y, endLineEndPoint.x - endLineStartPoint.x);

  const smoothBezier = getSmoothBezier(startLineEndPoint, startLineAngle, endLineStartPoint, endLineAngle);

  return {
    startControlPoint: {
      x: toFixed1(smoothBezier.startControlPoint.x),
      y: toFixed1(smoothBezier.startControlPoint.y)
    },
    endControlPoint: {
      x: toFixed1(smoothBezier.endControlPoint.x),
      y: toFixed1(smoothBezier.endControlPoint.y)
    }
  };
}

相关文章

  • Android贝塞尔曲线相关资料

    资料整理: Android-贝塞尔曲线 贝塞尔曲线扫盲 练习贝塞尔曲线 贝塞尔曲线绘制在线演示(带坐标) 生成三阶...

  • 模拟贝塞尔曲线

    二次贝塞尔曲线 可以快速生成二次、三次贝塞尔曲线的源码生成器,方便经常使用到canvas画图的同学使用,可以直接预...

  • Sketch·每日一临摹·2016/01

    160116·BayMax 钢笔工具注意事项 Straight:不使用贝塞尔曲线。Mirrored:使用贝塞尔曲线...

  • useDanmu 弹幕

    弹幕轨迹 1 .直接鼠标拖拽生成-可以直接得到svg轨迹,进行移动,但是不会很平滑2 .还是生成贝塞尔曲线这样更好...

  • 贝塞尔 平滑曲线

    本次阅读大概需要5分钟先看效果 简单说明一下(对名称不清楚的都能在下文中找到,不要着急)1.本案例适用于类似给定每...

  • 购物车动画

    使用贝塞尔曲线实现购物车抛物线动画关键步骤 使用path 构建贝塞尔曲线 使用PathMeasure计算曲线上每个...

  • [前端]使用曲线将多点连成一条平滑的曲线

    之前在写一个项目需要把多点连成平滑的曲线,而且这些点是无法预知的。开始想到用贝塞尔曲线,但是具体贝塞尔曲线的控制点...

  • ios知识点(7)贝塞尔曲线

    贝塞尔曲线扫盲iOS UIBezierPath贝塞尔曲线常用方法iOS UIBezierPath(贝塞尔曲线)iO...

  • 贝塞尔曲线原理及在iOS中使用介绍

    贝塞尔曲线是指可以通过一些控制点去控制曲线的形状并且保持曲线的平滑特性,不会让人感觉到突兀。在iOS开发中,贝塞尔...

  • Android 使用贝塞尔曲线将多点连成一条平滑的曲线

    本文主要讲解怎么用贝塞尔曲线将多点连成一条平滑的曲线,若不了解贝塞尔曲线的同学可以查看这里 先看效果 确定控制点 ...

网友评论

      本文标题:js:使用规划算法思想生成平滑贝塞尔曲线

      本文链接:https://www.haomeiwen.com/subject/gmeogdtx.html