我需要在三次贝塞尔曲线上找到一个点及其Angular ,可以使用JavaScript动态更改. 我向ChatGPT询问了这一点,它生成了以下代码,但Angular 计算不正确,我在哪里?还是ChatGPT错了?

      // Initialize with some initial control points
      let points = [
        { x: 50, y: 100 }, // Start point
        { x: 150, y: 50 }, // First control point
        { x: 250, y: 150 }, // Second control point
        { x: 350, y: 100 } // End point
      ];

      function deCasteljau(points, t) {
        if (points.length === 1) {
          return points[0];
        }

        const newPoints = [];
        for (let i = 0; i < points.length - 1; i++) {
          const x = (1 - t) * points[i].x + t * points[i + 1].x;
          const y = (1 - t) * points[i].y + t * points[i + 1].y;
          newPoints.push({ x, y });
        }

        return deCasteljau(newPoints, t);
      }

      function cubicBezierDerivative(points, t) {
        const derivativePoints = [];
        const n = points.length - 1;
        for (let i = 0; i < n; i++) {
            const dx = n * (points[i + 1].x - points[i].x);
            const dy = n * (points[i + 1].y - points[i].y);
            derivativePoints.push({ x: dx, y: dy });
        }
        return derivativePoints;
      }


      function bezierAngle(points, t) {
        const dPoints = cubicBezierDerivative(points, t);
        const point = deCasteljau(points, t);
        const dx = dPoints[0].x;
        const dy = dPoints[0].y;
        const radian = Math.atan2(dy, dx);
        //const angle = radian*180/Math.PI;
        return radian;
      }
      const point = deCasteljau(points, 0.9);
          
      const angle = bezierAngle(points, 0.9);


现场演示:

const canvas = document.getElementById('splineCanvas');
const ctx = canvas.getContext('2d');

let points = []; // Array to hold control points
let selectedPointIndex = -1; // Index of the currently selected control point

// Event listener for mouse down to select control point
canvas.addEventListener('mousedown', function(event) {
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    // Check if mouse is over any control point
    for (let i = 0; i < points.length; i++) {
        const dx = points[i].x - mouseX;
        const dy = points[i].y - mouseY;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < 6) { // 6 is the radius for selecting control point
            selectedPointIndex = i;
            canvas.addEventListener('mousemove', onMouseMove);
            canvas.addEventListener('mouseup', onMouseUp);
            break;
        }
    }
});

// Event listener for mouse move to update control point position
function onMouseMove(event) {
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    points[selectedPointIndex].x = mouseX;
    points[selectedPointIndex].y = mouseY;
    drawSpline();
}

// Event listener for mouse up to stop updating control point position
function onMouseUp() {
    canvas.removeEventListener('mousemove', onMouseMove);
    canvas.removeEventListener('mouseup', onMouseUp);
    selectedPointIndex = -1;
}

let testAngle = 65;

// Draw spline function
function drawSpline() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);

    for (let i = 1; i < points.length - 2; i+=3) {
        ctx.bezierCurveTo(
          points[i].x, 
          points[i].y,
          points[i+1].x,
          points[i+1].y,
          points[i+2].x,
          points[i+2].y,
        );
    }

    ctx.stroke();

    // Draw control points
    for (const point of points) {
        ctx.beginPath();
        ctx.arc(point.x, point.y, 6, 0, Math.PI * 2);
        ctx.fillStyle = "#ff0000";
        ctx.fill();
        ctx.closePath();
    }

    const point = deCasteljau(points, 0.9);
    //console.log('point = ', point);
    const angle = bezierAngle(points, 0.9);
    ctx.save();
    ctx.translate(point.x, point.y);
    ctx.rotate(angle);
    ctx.translate(-point.x, -point.y);

    ctx.fillStyle = "green";
    ctx.fillRect(point.x-5, point.y-5, 10, 10);
    ctx.restore();
}

// Initialize with some initial control points
points = [
  { x: 50, y: 100 }, // Start point
  { x: 150, y: 50 }, // First control point
  { x: 250, y: 150 }, // Second control point
  { x: 350, y: 100 } // End point
];

function deCasteljau(points, t) {
  if (points.length === 1) {
    return points[0];
  }

  const newPoints = [];
  for (let i = 0; i < points.length - 1; i++) {
    const x = (1 - t) * points[i].x + t * points[i + 1].x;
    const y = (1 - t) * points[i].y + t * points[i + 1].y;
    newPoints.push({ x, y });
  }

  return deCasteljau(newPoints, t);
}

function cubicBezierDerivative(points, t) {
  const derivativePoints = [];
  const n = points.length - 1;
  for (let i = 0; i < n; i++) {
      const dx = n * (points[i + 1].x - points[i].x);
      const dy = n * (points[i + 1].y - points[i].y);
      derivativePoints.push({ x: dx, y: dy });
  }
  return derivativePoints;
}


function bezierAngle(points, t) {
  const dPoints = cubicBezierDerivative(points, t);
  const point = deCasteljau(points, t);
  const dx = dPoints[0].x;
  const dy = dPoints[0].y;
  const radian = Math.atan2(dy, dx);
  //const angle = radian*180/Math.PI;
  return radian;
}

drawSpline();
<canvas id="splineCanvas" width="600" height="300"></canvas>

推荐答案

从数学的Angular 来看,我质疑你所说的"找到一个点及其Angular "是什么意思,就像你在点数组中所表示的那样,在2D空间中,点只有{x, y}个,没有Angular ……


我们能做的就是计算两个点之间的Angular ,看起来这就是函数cubicBezierDerivativebezierAngle试图做的事情,我假设这就是你需要/问的,我会基于这一点来编写下面的代码,同时我也会假设function deCasteljau返回的点是正确的,我不会花费任何时间来研究这是如何做到的.

所以我们可以修改function bezierAngle来返回两个给定点之间的Angular ,当前点和我所说的"下一个"点:

    const point = deCasteljau(points, x);
    const next = deCasteljau(points, x + 0.01);
    const angle = bezierAngle([point, next]);

根据这两个元素之间的Angular ,我们绘制的正方形是以正确的方式"朝向"的.

在下面的代码中,您将看到一个新的函数drawRect,它是我们绘制正方形的地方,并且因为它现在是一个函数,所以我们可以有多个不同 colored颜色 的正方形

const canvas = document.getElementById('splineCanvas');
const ctx = canvas.getContext('2d');

let points = [
  { x: 50,  y: 100 }, // Start point
  { x: 170, y: 20  }, // First control point
  { x: 240, y: 170 }, // Second control point
  { x: 350, y: 10  }  // End point
]
let selectedPointIndex = -1;

// Event listener for mouse down to select control point
canvas.addEventListener('mousedown', function(event) {
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    // Check if mouse is over any control point
    for (let i = 0; i < points.length; i++) {
        const dx = points[i].x - mouseX;
        const dy = points[i].y - mouseY;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < 6) { // 6 is the radius for selecting control point
            selectedPointIndex = i;
            canvas.addEventListener('mousemove', onMouseMove);
            canvas.addEventListener('mouseup', onMouseUp);
            break;
        }
    }
});

// Event listener for mouse move to update control point position
function onMouseMove(event) {
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    points[selectedPointIndex].x = mouseX;
    points[selectedPointIndex].y = mouseY;
    drawSpline();
}

// Event listener for mouse up to stop updating control point position
function onMouseUp() {
    canvas.removeEventListener('mousemove', onMouseMove);
    canvas.removeEventListener('mouseup', onMouseUp);
    selectedPointIndex = -1;
}

let testAngle = 65;

// Draw spline function
function drawSpline() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);

    ctx.bezierCurveTo(
      points[1].x, points[1].y,
      points[2].x, points[2].y,
      points[3].x, points[3].y,
    );
    ctx.stroke();

    // Draw control points
    for (const point of points) {
        ctx.beginPath();
        ctx.arc(point.x, point.y, 6, 0, Math.PI * 2);
        ctx.fillStyle = "#ff0000";
        ctx.fill();
        ctx.closePath();
    }
   drawRect(0.2, "cyan")
   drawRect(0.5, "blue")
   drawRect(0.8, "green")
}

function drawRect(x, color) {
    const point = deCasteljau(points, x);
    const next = deCasteljau(points, x + 0.01);
    const angle = bezierAngle([point, next]);
    ctx.save();
    ctx.globalAlpha = 0.6
    ctx.translate(point.x, point.y);
    ctx.rotate(angle);
    ctx.translate(-point.x, -point.y);

    ctx.fillStyle = color;
    ctx.fillRect(point.x-15, point.y-15, 30, 30);
    //ctx.fillStyle = "black";
    //ctx.fillText(angle, point.x, point.y);
    ctx.restore();
}

function deCasteljau(points, t) {
  if (points.length === 1) {
    return points[0];
  }

  const newPoints = [];
  for (let i = 0; i < points.length - 1; i++) {
    const x = (1 - t) * points[i].x + t * points[i + 1].x;
    const y = (1 - t) * points[i].y + t * points[i + 1].y;
    newPoints.push({ x, y });
  }

  return deCasteljau(newPoints, t);
}

function bezierAngle(points) {
  const dx = points[1].x - points[0].x;
  const dy = points[1].y - points[0].y;
  const radian = Math.atan2(dy, dx);
  return radian;
}

drawSpline();
<canvas id="splineCanvas" width="400" height="180"></canvas>

Javascript相关问答推荐

JavaScript文本区域阻止KeyDown/KeyUp事件本身上的Alt GR +键组合

有Angular 的material .未应用收件箱中的价值变化

是什么原因导致此Angular 16应用程序中类型错误时属性结果不存在?

无法在nextjs应用程序中通过id从mongoDB删除'

如何使用JavaScript将文本插入空div

当试图显示小部件时,使用者会出现JavaScript错误.

将旋转的矩形zoom 到覆盖它所需的最小尺寸&S容器

如何在bslib nav_insert之后更改导航标签的CSS类和样式?

我在Django中的视图中遇到多值键错误

material UI按钮组样式props 不反射

类构造函数不能在没有用With Router包装的情况下调用

Eval vs函数()返回语义

自定义确认组件未在vue.js的v菜单内打开

在JS中动态创建对象,并将其追加到HTML表中

每次重新呈现时调用useState initialValue函数

警告框不显示包含HTML输入字段的总和

在Puppeteer中使用promise进行日志(log)记录时出现TargetCloseError

在传单的图像覆盖中重新着色特定 colored颜色 的所有像素

输入数据覆盖JSON文件

在使用JavaScript以HTML格式显示Google Drive中的图像时遇到问题