这个描述有点复杂,所以请耐心等待。
我使用HTML5画布扩展了图表工具(Diagramo)。它实现了多种类型的直线,直线,锯齿(直角)和弯曲(立方或二次)。这些线可以是实线,点线或虚线。
我正在实施的新功能是"波浪形的"线,其中而不是遵循恒定路径,线在平滑弧中在所需目标路径上来回曲折。
以下是一个正确的例子。这在大多数情况下都适用,但在某些边缘情况下则不然。
实现是采用曲线,使用二次或三次函数来估计沿线的等距离点,并通过在直线的两侧放置控制点(交替)并绘制多个立方体来沿这些直线绘制波浪线曲线。
当线条相对较短时会出现问题,并且在靠近原点时会自行加倍。下面是一个例子,这种情况也发生在更长的线上 - 关键点是在原点之后有一条非常小的尖锐曲线。在这种情况下,算法选择尖锐曲线后的第一个点,在某些情况下紧邻原点,并考虑第一个段。
每个波浪线的最小/最大像素长度为8px / 14px(我可以更改,但远低于此值,它变得太尖锐,上面变得过于波浪状)代码试图为线条找到正确大小的波浪线段以适应最小的空白区域,然后用直线填充。
我希望有一个解决方案可以解释急剧弯曲的线条,如果我知道沿着一条线的所有点,我可以选择交替线的两侧的控制点,也垂直吗?
一种选择是考虑点i
和点i-1
和i+1
并使用它来确定线的方向,从而选择控制点吗?
代码如下
//fragment is either Cubic or Quadratic curve.
paint(fragment){
var length = fragment.getLength();
var points = Util.equidistancePoints(fragment, length < 100 ? (length < 50 ? 3: 5): 11);
points.splice(0, 1); //remove origin as that is the initial point of the delegate.
//points.splice(0, 1);
delegate.paint(context, points);
}
/**
*
* @param {QuadCurve} or {CubicCurbe} curve
* @param {Number} m the number of points
* @return [Point] a set of equidistance points along the polyline of points
* @author Zack
* @href http://math.stackexchange.com/questions/321293/find-coordinates-of-equidistant-points-in-bezier-curve
*/
equidistancePoints: function(curve, m){
var points = curve.getPoints(0.001);
// Get fractional arclengths along polyline
var n = points.length;
var s = 1.0/(n-1);
var dd = [];
var cc = [];
var QQ = [];
function findIndex(dd, d){
var i = 0;
for (var j = 0 ; j < dd.length ; j++){
if (d > dd[j]) {
i = j;
}
else{
return i;
}
}
return i;
};
dd.push(0);
cc.push(0);
for (var i = 0; i < n; i++){
if(i >0) {
cc.push(Util.distance(points[i], points[i - 1]));
}
}
for (var i = 1 ; i < n ; i++) {
dd.push(dd[i-1] + cc[i]);
}
for (var i = 1 ; i < n ; i++) {
dd[i] = dd[i]/dd[n-1];
}
var step = 1.0/(m-1);
for (var r = 0 ; r < m ; r++){
var d = parseFloat(r)*step;
var i = findIndex(dd, d);
var u = (d - dd[i]) / (dd[i+1] - dd[i]);
var t = (i + u)*s;
QQ[r] = curve.getPoint(t);
}
return QQ;
}
SquigglyLineDelegate.prototype = {
constructor: SquigglyLineDelegate,
paint: function(context, points){
var squiggles = 0;
var STEP = 0.1;
var useStart = false;
var bestSquiggles = -1;
var bestA = 0;
var distance = Util.distance(points[0], this.start);
for(var a = SquigglyLineDelegate.MIN_SQUIGGLE_LENGTH; a < SquigglyLineDelegate.MAX_SQUIGGLE_LENGTH; a += STEP){
squiggles = distance / a;
var diff = Math.abs(Math.floor(squiggles) - squiggles);
if(diff < bestSquiggles || bestSquiggles == -1){
bestA = a;
bestSquiggles = diff;
}
}
squiggles = distance / bestA;
for(var i = 0; i < points.length; i++){
context.beginPath();
var point = points[i];
for(var s = 0; s < squiggles-1; s++){
var start = Util.point_on_segment(this.start, point, s * bestA);
var end = Util.point_on_segment(this.start, point, (s + 1) * bestA);
var mid = Util.point_on_segment(this.start, point, (s + 0.5) * bestA);
end.style.lineWidth = 1;
var line1 = new Line(Util.point_on_segment(mid, end, -this.squiggleWidth), Util.point_on_segment(mid, end, this.squiggleWidth));
var mid1 = Util.getMiddle(line1.startPoint, line1.endPoint);
line1.transform(Matrix.translationMatrix(-mid1.x, -mid1.y));
line1.transform(Matrix.rotationMatrix(radians = 90 * (Math.PI/180)));
line1.transform(Matrix.translationMatrix(mid1.x, mid1.y));
var control1 = useStart ? line1.startPoint : line1.endPoint;
var curve = new QuadCurve(start, control1, end);
curve.style = null;
curve.paint(context);
useStart = !useStart;
}
this.start = point;
context.lineTo(point.x, point.y);
context.stroke();
}
}
}