gpt4 book ai didi

svg - 如何在SVG中使用贝塞尔路径近似半余弦曲线?

转载 作者:行者123 更新时间:2023-12-04 00:05:06 25 4
gpt4 key购买 nike

假设我想使用贝塞尔曲线在 SVG 中近似一条半余弦曲线。半余弦应如下所示:

half cosine

并从 [x0,y0](左侧控制点)运行到 [x1,y1](右侧控制点)。

我怎样才能找到一组可接受的系数来很好地逼近这个函数?

奖金问题 :如何概括公式,例如,四分之一的余弦?

请注意我 不要想要用一系列相互连接的线段来近似余弦,我想使用贝塞尔曲线计算一个很好的近似值。

我在评论中尝试了解决方案,但是,有了这些系数,曲线似乎在第二个点之后结束。

最佳答案

假设您要保持两端的切线水平。所以很自然地,解决方案将是对称的,归结为在水平方向上找到第一个控制点。

我写了一个程序来做到这一点:

/*
* Find the best cubic Bézier curve approximation of a sine curve.
*
* We want a cubic Bézier curve made out of points (0,0), (0,K), (1-K,1), (1,1) that approximates
* the shifted sine curve (y = a⋅sin(bx + c) + d) which has its minimum at (0,0) and maximum at (1,1).
* This is useful for CSS animation functions.
*
* ↑ P2 P3
* 1 ו••••••***×
* | ***
* | **
* | *
* | **
* | ***
* ×***•••••••×------1-→
* P0 P1
*/

const sampleSize = 10000; // number of points to compare when determining the root-mean-square deviation
const iterations = 12; // each iteration gives one more digit

// f(x) = (sin(π⋅(x - 1/2)) + 1) / 2 = (1 - cos(πx)) / 2
const f = x => (1 - Math.cos(Math.PI * x)) / 2;

const sum = function (a, b, c) {
if (Array.isArray(c)) {
return [...arguments].reduce(sum);
}
return [a[0] + b[0], a[1] + b[1]];
};

const times = (c, [x0, x1]) => [c * x0, c * x1];

// starting points for our iteration
let [left, right] = [0, 1];
for (let digits = 1; digits <= iterations; digits++) {
// left and right are always integers (digits after 0), this keeps rounding errors low
// In each iteration, we divide them by a higher power of 10
let power = Math.pow(10, digits);
let min = [null, Infinity];
for (let K = 10 * left; K <= 10 * right; K+= 1) { // note that the candidates for K have one more digit than previous `left` and `right`
const P1 = [K / power, 0];
const P2 = [1 - K / power, 1];
const P3 = [1, 1];

let bezierPoint = t => sum(
times(3 * t * (1 - t) * (1 - t), P1),
times(3 * t * t * (1 - t), P2),
times(t * t * t, P3)
);

// determine the error (root-mean-square)
let squaredErrorSum = 0;
for (let i = 0; i < sampleSize; i++) {
let t = i / sampleSize / 2;
let P = bezierPoint(t);
let delta = P[1] - f(P[0]);
squaredErrorSum += delta * delta;
}
let deviation = Math.sqrt(squaredErrorSum); // no need to divide by sampleSize, since it is constant

if (deviation < min[1]) {
// this is the best K value with ${digits + 1} digits
min = [K, deviation];
}
}
left = min[0] - 1;
right = min[0] + 1;
console.log(`.${min[0]}`);
}


为了简化计算,我使用了经过 (0,0) 的归一化正弦曲线。和 (1,1)作为其最小/最大点。这对于 CSS 动画也很有用。

它返回 (.3642124232,0) * 作为均方根偏差最小的点(大约 0.00013)。

我还创建了一个 Desmos graph这显示了准确性:

Desmos Graph (sine approximation with cubic Bézier curve)
(点击试试——可以左右拖动控制点)

* 注意用JS做数学有四舍五入的误差,所以数值大概准确到不超过5位左右。

关于svg - 如何在SVG中使用贝塞尔路径近似半余弦曲线?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29022438/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com