Paper.js 从一条路径中画出多条平行路径
Paper.js draw multiple parallel paths from one path
我正在尝试根据一组坐标绘制多条平行路径,如本例所示:
我根据一组段创建了我的路径,然后我将它克隆了五次,然后这样翻译:
var myPath;
var lineData = []; // Long array of segments
myPath.segments = lineData;
for (var i = 1; i < 5; i++) {
var clone = myPath.clone();
clone.translate(new paper.Point(0, i*5));
}
这是我得到的结果:
我希望线条完全平行,但距离总是不同,有时它们会重叠。有没有办法修复它,或者我应该尝试不同的方法来创建这种曲线?
一条三次贝塞尔曲线不能展开为另一条平行的三次贝塞尔曲线。
@Blindman67 沿原始曲线计算法线(垂直于切线)并在这些垂直线上画点的方法将起作用。
要开始,请参阅此 SO Q&A,它显示了计算垂直于贝塞尔曲线的点的算法。
这是一个概念验证示例:
如果您只想要实心平行曲线,则通过将 tCount
设置得更高(例如 tCount=500
)来过采样。此概念验证使用点来创建线,但对于实曲线,您可以使用一组线点。
To-Do if you want to dotted lines:你需要使用算法进行过采样(可能使用 tCount=500
而不是 60)并删除重复数据得到的点集,直到沿着曲线有均匀距离的点。这样你的点就不会稀疏地沿着曲线聚集。
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// variables defining a cubic bezier curve
var PI2=Math.PI*2;
var s={x:20,y:30};
var c1={x:200,y:40};
var c2={x:40,y:200};
var e={x:270,y:220};
// an array of points plotted along the bezier curve
var points=[];
// we use PI often so put it in a variable
var PI=Math.PI;
// plot 60 points along the curve
// and also calculate the angle of the curve at that point
var tCount=60;
for(var t=0;t<=tCount;t++){
var T=t/tCount;
// plot a point on the curve
var pos=getCubicBezierXYatT(s,c1,c2,e,T);
// calculate the perpendicular angle of the curve at that point
var tx = bezierTangent(s.x,c1.x,c2.x,e.x,T);
var ty = bezierTangent(s.y,c1.y,c2.y,e.y,T);
var a = Math.atan2(ty, tx)-PI/2;
// save the x/y position of the point and the perpendicular angle
// in the points array
points.push({
x:pos.x,
y:pos.y,
angle:a
});
}
var PI2=Math.PI*2;
var radii=[-12,-6,0,6,12];
// fill the background
ctx.fillStyle='navy';
ctx.fillRect(0,0,cw,ch);
// draw a dots perpendicular to each point on the curve
ctx.beginPath();
for(var i=0;i<points.length;i++){
for(var j=-2;j<3;j++){
var r=radii[j+2];
var x=points[i].x+r*Math.cos(points[i].angle);
var y=points[i].y+r*Math.sin(points[i].angle);
ctx.moveTo(x,y);
ctx.arc(x,y,1.5,0,PI2);
}
}
ctx.fillStyle='skyblue';
ctx.fill();
//////////////////////////////////////////
// helper functions
//////////////////////////////////////////
// calculate one XY point along Cubic Bezier at interval T
// (where T==0.00 at the start of the curve and T==1.00 at the end)
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
// calculate the perpendicular angle at interval T on the curve
function bezierTangent(a, b, c, d, t) {
return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
};
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Dotted parallel Bezier Curve.</h4>
<canvas id="canvas" width=300 height=300></canvas>
我正在尝试根据一组坐标绘制多条平行路径,如本例所示:
我根据一组段创建了我的路径,然后我将它克隆了五次,然后这样翻译:
var myPath;
var lineData = []; // Long array of segments
myPath.segments = lineData;
for (var i = 1; i < 5; i++) {
var clone = myPath.clone();
clone.translate(new paper.Point(0, i*5));
}
这是我得到的结果:
我希望线条完全平行,但距离总是不同,有时它们会重叠。有没有办法修复它,或者我应该尝试不同的方法来创建这种曲线?
一条三次贝塞尔曲线不能展开为另一条平行的三次贝塞尔曲线。
@Blindman67 沿原始曲线计算法线(垂直于切线)并在这些垂直线上画点的方法将起作用。
要开始,请参阅此 SO Q&A,它显示了计算垂直于贝塞尔曲线的点的算法。
这是一个概念验证示例:
如果您只想要实心平行曲线,则通过将 tCount
设置得更高(例如 tCount=500
)来过采样。此概念验证使用点来创建线,但对于实曲线,您可以使用一组线点。
To-Do if you want to dotted lines:你需要使用算法进行过采样(可能使用 tCount=500
而不是 60)并删除重复数据得到的点集,直到沿着曲线有均匀距离的点。这样你的点就不会稀疏地沿着曲线聚集。
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// variables defining a cubic bezier curve
var PI2=Math.PI*2;
var s={x:20,y:30};
var c1={x:200,y:40};
var c2={x:40,y:200};
var e={x:270,y:220};
// an array of points plotted along the bezier curve
var points=[];
// we use PI often so put it in a variable
var PI=Math.PI;
// plot 60 points along the curve
// and also calculate the angle of the curve at that point
var tCount=60;
for(var t=0;t<=tCount;t++){
var T=t/tCount;
// plot a point on the curve
var pos=getCubicBezierXYatT(s,c1,c2,e,T);
// calculate the perpendicular angle of the curve at that point
var tx = bezierTangent(s.x,c1.x,c2.x,e.x,T);
var ty = bezierTangent(s.y,c1.y,c2.y,e.y,T);
var a = Math.atan2(ty, tx)-PI/2;
// save the x/y position of the point and the perpendicular angle
// in the points array
points.push({
x:pos.x,
y:pos.y,
angle:a
});
}
var PI2=Math.PI*2;
var radii=[-12,-6,0,6,12];
// fill the background
ctx.fillStyle='navy';
ctx.fillRect(0,0,cw,ch);
// draw a dots perpendicular to each point on the curve
ctx.beginPath();
for(var i=0;i<points.length;i++){
for(var j=-2;j<3;j++){
var r=radii[j+2];
var x=points[i].x+r*Math.cos(points[i].angle);
var y=points[i].y+r*Math.sin(points[i].angle);
ctx.moveTo(x,y);
ctx.arc(x,y,1.5,0,PI2);
}
}
ctx.fillStyle='skyblue';
ctx.fill();
//////////////////////////////////////////
// helper functions
//////////////////////////////////////////
// calculate one XY point along Cubic Bezier at interval T
// (where T==0.00 at the start of the curve and T==1.00 at the end)
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
// calculate the perpendicular angle at interval T on the curve
function bezierTangent(a, b, c, d, t) {
return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
};
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Dotted parallel Bezier Curve.</h4>
<canvas id="canvas" width=300 height=300></canvas>