我重新格式化了一些我发现使用基数样条曲线的代码并绘制了一条曲线,给出了一组可与我的Canvas库一起使用的点,它工作得非常好,但后来我还想使用所述技术沿给定的集合移动对象点 - 一条路。 SO有几个与我的问题有关的问题,我试图实现this问题的最后一个答案,但老实说,我不知道他的代码中的一半变量是什么意思。这是我的library和curve object本身:
Art.prototype.modules.display.Base.extend({
constructor: function (options) {
// Declare variables for brevity.
var extend = Art.prototype.modules.utility.extend,
defaults = {
points: [],
tension: 0.5,
closed: false
};
// Extend the object with the defaults overwritten by the options.
extend(this, extend(defaults, options));
},
id: 'curve',
draw: function () {
// Declare variables for brevity.
var t = this,
graphics = Art.prototype.modules.display.curve.core.graphics,
controls = [],
n = t.points.length,
getControlPoints = function (a, b, c, d, e, f, tension) {
var x = {
x: Math.sqrt(Math.pow(c - a, 2) + Math.pow(d - b, 2)),
y: Math.sqrt(Math.pow(e - c, 2) + Math.pow(f - d, 2))
};
var y = {
x: tension * x.x / (x.x + x.y)
};
y.y = tension - y.x;
var z = {
x: c + y.x * (a - e),
y: d + y.x * (b - f)
};
var $z = {
x: c - y.y * (a - e),
y: d - y.y * (b - f)
};
return [z.x, z.y, $z.x, $z.y];
};
graphics.strokeStyle = t.stroke;
graphics.lineWidth = t.lineWidth;
if (t.closed) {
t.points.push(t.points[0], t.points[1], t.points[2], t.points[3]);
t.points.unshift(t.points[n - 1]);
t.points.unshift(t.points[n - 1]);
for (var p = 0; p < n; p += 2) {
controls = controls.concat(getControlPoints(t.points[p], t.points[p + 1], t.points[p + 2], t.points[p + 3], t.points[p + 4], t.points[p + 5], t.tension));
}
controls = controls.concat(controls[0], controls[1]);
for (var $p = 2; $p < n + 2; $p += 2) {
graphics.beginPath();
graphics.moveTo(t.points[$p], t.points[$p + 1]);
graphics.bezierCurveTo(controls[2 * $p - 2], controls[2 * $p - 1], controls[2 * $p], controls[2 * $p + 1], t.points[$p + 2], t.points[$p + 3]);
graphics.stroke();
graphics.closePath();
}
} else {
for (var p = 0; p < n - 4; p += 2) {
controls = controls.concat(getControlPoints(t.points[p], t.points[p + 1], t.points[p + 2], t.points[p + 3], t.points[p + 4], t.points[p + 5], t.tension));
}
for (var $p = 2; $p < t.points.length - 5; $p += 2) {
graphics.beginPath();
graphics.moveTo(t.points[$p], t.points[$p + 1]);
graphics.bezierCurveTo(controls[2 * $p - 2], controls[2 * $p - 1], controls[2 * $p], controls[2 * $p + 1], t.points[$p + 2], t.points[$p + 3]);
graphics.stroke();
graphics.closePath();
}
graphics.beginPath();
graphics.moveTo(t.points[0], t.points[1]);
graphics.quadraticCurveTo(controls[0], controls[1], t.points[2], t.points[3]);
graphics.stroke();
graphics.closePath();
graphics.beginPath();
graphics.moveTo(t.points[n - 2], t.points[n - 1]);
graphics.quadraticCurveTo(controls[2 * n - 10], controls[2 * n - 9], t.points[n - 4], t.points[n - 3]);
graphics.stroke();
graphics.closePath();
}
return this;
}
});
我不一定希望代码在银盘上交给我(虽然......那会很好) - 相反,我想学习所涉及的数学,但最好是用伪代码和相对简单的术语。我联系到的SO答案的解释会特别有用,因为它很好用。
答案 0 :(得分:1)
使用替代实现(https://gitlab.com/epistemex/cardinal-spline-js免责声明:我是作者)将以简单的方式在您需要的路径上生成所有点。
在将点作为样条点阵列获得之后,主函数将遍历数组以找到所需位置所在的两个点的段。接下来,它将在这些位置之间插值以获得最终(x,y)位置(这里有足够的优化空间):
这允许我们以均匀的速度沿着样条移动 -
function getXY(points, pos, length) {
var len = 0, // total length so far
lastLen, // last segment length
i, // iterator
l = points.length; // length of point array
// find segment
for(i = 2; i < l; i += 2) {
// calculate length of this segment
lastLen = dist(points[i], points[i+1], points[i-2], points[i-1]);
// add to total length for now
len += lastLen;
// are we inside a segment?
if (pos < len && lastLen) {
len -= lastLen; // to back to beginning
pos -= len; // adjust position so we can normalize
return {
// interpolate prev X + (next X - prev X) * normalized
x: points[i-2] + (points[i] - points[i-2]) * (pos / lastLen),
y: points[i-1] + (points[i+1] - points[i-1]) * (pos / lastLen)
}
}
}
}
var ctx = document.querySelector("canvas").getContext("2d"),
points = [
10, 10, // x,y pairs
100, 50,
500, 100,
600, 200,
400, 220,
200, 90
],
spline = getCurvePoints(points),
length = getLength(spline),
t = 0,
dx = 3; // number of pixels to move object
// move along path:
(function loop() {
// make t ping-pong, and clamp t to [0, (length-1)]
t += dx;
if (t < 0 || t >= length) dx = -dx;
t = Math.max(0, Math.min(length - 1, t));
// find segment in points which t is inside:
var pos = getXY(spline, t, length);
// redraw
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
render();
// show marker
ctx.fillRect(pos.x - 3, pos.y - 3, 6, 6);
requestAnimationFrame(loop)
})();
function render(points) {
ctx.beginPath();
ctx.moveTo(spline[0], spline[1]);
for(var i = 2; i < spline.length; i+=2)
ctx.lineTo(spline[i], spline[i+1]);
ctx.stroke()
}
function getXY(points, pos, length) {
var len = 0, lastLen, i, l = points.length;
// find segment
for(i = 2; i < l; i += 2) {
lastLen = dist(points[i], points[i+1], points[i-2], points[i-1]);
len += lastLen;
if (pos < len && lastLen) {
len -= lastLen;
pos -= len;
return {
x: points[i-2] + (points[i] - points[i-2]) * (pos / lastLen),
y: points[i-1] + (points[i+1] - points[i-1]) * (pos / lastLen)
}
}
}
return null
}
function getLength(points) {
for(var len = 0, i = 0, dx, dy; i < points.length - 2; i+=2) {
len += dist(points[i+2], points[i+3], points[i], points[i+1])
}
return len
}
function dist(x1, y1, x2, y2) {
var dx = x2 - x1,
dy = y2 - y1;
return Math.sqrt(dx*dx + dy*dy)
}