繁体   English   中英

如何将三次贝塞尔曲线细分为点

[英]How to subdivide Cubic Bezier curve into points

我正在使用由年度数据组成的数据集。 数组中的每个项目对应于一年。 我想扩展这个数据集并改为每月制作一次。 我一直在研究它并尝试不同的方法,但最终找到了 De Casteljau 的算法来确定贝塞尔曲线上的各个点。

https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm

如果你可以看看这里的例子。 http://jsfiddle.net/vdbhc98j/

const data = [29.9, 55.5, 106.4, 1129.2, 344.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4];

// https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
const getLinearInterpolatedValue = (t: number, p1: number, p2: number) => (1 - t) * p1 + t * p2;

const getLinearInterpolatedSegment = (...args: number[]): number[] => {
  const [t, p1, p2, ...rest] = args;
  return rest.length > 0
    ? [getLinearInterpolatedValue(t, p1, p2), ...getLinearInterpolatedSegment(t, p2, ...rest)]
    : [getLinearInterpolatedValue(t, p1, p2)];
};

const findPointOnBezierCurve = (t: number, ps: number[]): number =>
  ps.length > 1 ? findPointOnBezierCurve(t, getLinearInterpolatedSegment(t, ...ps)) : ps[0];

const subdividePoints = (points: number[], years: number) => {
  if (points.length === 0 || years === 0) {
    return points;
  }
  const step = 1 / years / 12;
  const newPoints = [];
  for (let i = 0; i <= years * 12; i++) {
    newPoints.push(findPointOnBezierCurve(step * i, points));
  }
  return newPoints;
};

const chart = new Highcharts.Chart({
  chart: {
    renderTo: 'container',
    type: 'spline'
  },
  series: [{
    data,
  }, {
    type: 'spline',
    data: []
  }]
});


const subdivided = new Highcharts.Chart({
  chart: {
    renderTo: 'container2',
    type: 'spline'
  },
  series: [{
    data: subdividePoints(data, 10)
  }, {
    type: 'spline',
    data: []
  }]
});

即使存在相似之处,它确实将曲线细分为 120 个不同的点,但它并没有完全保持曲线。

平滑值和丢失值

如果您还检查这些值,在没有细分的原始图表中,该值会上升到 1129,而在细分的图表中,它会上升到 ~427。

你能帮我吗 - 特别是我对算法的期望是错误的还是我在某个地方犯了错误? 如果你能指出这个问题是否有替代方案,那就太好了。

先感谢您。

我发现这个库叫做 Curve-Interpolator, https://www.npmjs.com/package/curve-interpolator

使用它的CurveInterpolator2D class,我能够生成 output:

http://jsfiddle.net/sznjxrah/1/

const points = [29.9, 55.5, 106.4, 1129.2, 344.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4].map((v, i) => [
  i * 100,
  v,
]);
const tension = 0;
const arcDivisions = 1;
const spline = new CurveInterpolator2D(points, tension, arcDivisions);

spline.getPoints(120); 
/**
[
    29.9,
    31.18203376736111,
    32.681006249999996,
    34.39622421875,
    36.326994444444445,
    38.47262369791667,
    40.83241875,
    43.40568637152778,
    46.19173333333333,
    49.189866406250005,
    52.39939236111111,
    55.78703431712963,
    55.19179999999999,
    49.24130734953704,
    40.12292546296298,
    30.024023437500006,
    21.131970370370375,
    15.634135358796314,
    15.717887500000032,
    23.570595891203705,
    41.37962962962962,
    71.33235781249996,
    115.8621483796296,
    184.80732167245378,
    277.6864000000003,
    388.07569082754674,
    509.55150162037035,
    635.6901398437502,
    760.0679129629629,
    876.2611284432877,
    977.8460937500004,
    1058.3991163483802,
    1111.4965037037043,
    1130.866071875,
    1116.176286111111,
    1073.0483072916666,
    1007.0862000000002,
    923.8940288194445,
    829.0758583333331,
    728.2357531249997,
    626.9777777777779,
    530.9059968749999,
    445.6244750000001,
    376.7372767361105,
    328.71915555555563,
    292.59375,
    264.22578888888887,
    242.48392222222216,
    226.23679999999996,
    214.3530722222223,
    205.70138888888889,
    199.15040000000005,
    193.56875555555564,
    187.8251055555556,
    180.78810000000004,
    171.830906394676,
    163.81327407407412,
    157.24243203125002,
    151.9466905092592,
    147.7543597511574,
    144.49374999999998,
    141.99317149884257,
    140.08093449074073,
    138.58534921874997,
    137.3347259259259,
    136.15737485532404,
    134.97710625,
    134.17229169560184,
    133.8071314814815,
    133.88555390624998,
    134.41148726851853,
    135.38885986689814,
    136.8216,
    138.7136359664352,
    141.06889606481482,
    143.89130859375,
    147.1848018518519,
    151.18287395833335,
    156.56722500000004,
    163.08477465277775,
    170.39999999999998,
    178.17737812500008,
    186.0813861111111,
    193.77650104166668,
    200.92719999999997,
    207.1979600694445,
    212.25325833333338,
    215.75757187500002,
    217.69051851851853,
    218.73166724537037,
    218.953125,
    218.38724247685184,
    217.06637037037035,
    215.02285937499997,
    212.28906018518515,
    208.8973234953704,
    204.87999999999994,
    200.2694403935184,
    195.09799537037037,
    189.00837890625004,
    181.429861111111,
    172.65016623263892,
    162.97778125000002,
    152.72119314236113,
    142.1888888888889,
    131.68935546875,
    121.5310798611111,
    112.02254904513886,
    103.47224999999997,
    96.18866970486107,
    90.02936921296303,
    84.45704453124995,
    79.43045925925931,
    74.91241009837958,
    70.86569375000003,
    67.25310691550922,
    64.03744629629625,
    61.18150859375002,
    58.64809050925927,
    56.39998874421293,
    54.4
]
*/

现在看起来像这样:

在此处输入图像描述

De Casteljau 算法将应用于控制点,这些控制点通常不在曲线上。 您的JS代码中使用的点数据是曲线上的点,与曲线的控制点不同。 因此,本质上,您的第二条曲线是一条贝塞尔曲线,其控制点来自第一条曲线上的点。 这就是为什么第 2 条曲线与第 1 条曲线的形状相似但峰值较小的原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM