2011-09-08 73 views
8

对于我的一个应用程序,我需要在Html5画布上的bezier路径上绘制虚线曲线......虚线之间的长度和间距应该是可变的......它是可以在JavaFx,see this link ...我想用Html5 canvas实现同样的效果。我知道如何绘制虚直线,但不是沿着贝塞尔曲线...Html5画布上的虚线曲线贝塞尔

虽然我不是专家,但我知道bezier drawing algorithm,我用这个算法看到的问题是,它可以让你识别坐标bezier使用的时间参数范围从0到1 ...

这是不够的,因为绘制虚线bezier,我需要绘制许多小bezier,具有指定的长度参数和给定的间隙距离,on主要的贝塞尔路径。必须有一些JavaFx使用的算法。如果任何人都可以帮助我,那会很棒。

+0

有一个聪明的执行类似的东西,你可能能够适应这里的虚曲线:http://stackoverflow.com/questions/ 4576724 /虚线笔画在这里生动的示例:http://phrogz.net/tmp/canvas_dashed_line.html – unmounted

+0

正如我所说,我知道如何绘制虚线,问题是如何在虚线上绘制虚线bezier路径... –

+0

我想你可以在你的贝兹绘制算法中使用mod op(%)。将alpha设置为偶数位置为零,并将曲线上奇数位置的alpha设置为相对于其长度的正常位置。如果你能向我提供你的贝塞尔算法,我不介意把这个数学计算在内。:) –

回答

4

我会假设JavaFX使用一种通用技术来绘制任何虚曲线,并且恰好在该示例中的bezier上使用它。

困难的部分是确定在哪里开始和停止每个短划线,这需要知道沿着它的各个点的贝塞尔曲线的arc length

有一种分析方法,但我会建议如下:

var bezier = function(controlPoints, t) { 
    /* your code here, I'll presume it returns a 2-element array of x and y. */ 
}; 

//just figure out the coordinates of all the points in each dash, don't draw. 
//returns an array of arrays, each sub-array will have an even number of nu- 
//merical elements, to wit, x and y pairs. 

//Argument dashPattern should be an array of alternating dash and space 
//lengths, e.g., [10, 10] would be dots, [30, 10] would be dashes, 
//[30, 10, 10, 10] would be 30-length dash, 10-length spaces, 10-length dash 
// and 10-length space. 
var calculateDashedBezier = function(controlPoints, dashPattern) { 
    var step = 0.001; //this really should be set by an intelligent method, 
        //rather than using a constant, but it serves as an 
        //example. 

    //possibly gratuitous helper functions 
    var delta = function(p0, p1) { 
    return [p1[0] - p0[0], p1[1] - p0[1]]; 
    }; 
    var arcLength = function(p0, p1) { 
    var d = delta(p0, p1); 
    return Math.sqrt(d[0]*d[0] + d[1] * d[1]); 
    }; 

    var subPaths = []; 
    var loc = bezier(controlPoints, 0); 
    var lastLoc = loc; 

    var dashIndex = 0; 
    var length = 0; 
    var thisPath = []; 
    for(var t = step; t <= 1; t += step) { 
    loc = bezier(controlPoints, t); 
    length += arcLength(lastLoc, loc); 
    lastLoc = loc; 

    //detect when we come to the end of a dash or space 
    if(length >= dashPattern[dashIndex]) { 

     //if we are on a dash, we need to record the path. 
     if(dashIndex % 2 == 0) 
     subPaths.push(thisPath); 

     //go to the next dash or space in the pattern 
     dashIndex = (dashIndex + 1) % dashPattern.length; 

     //clear the arclength and path. 
     thisPath = []; 
     length = 0; 
    } 

    //if we are on a dash and not a space, add a point to the path. 
    if(dashIndex % 2 == 0) { 
     thisPath.push(loc[0], loc[1]); 
    } 
    } 
    if(thisPath.length > 0) 
    subPaths.push(thisPath); 
    return subPaths; 
}; 

//take output of the previous function and build an appropriate path 
var pathParts = function(ctx, pathParts) { 
    for(var i = 0; i < pathParts.length; i++) { 
    var part = pathParts[i]; 
    if(part.length > 0) 
     ctx.moveTo(part[0], part[1]); 
    for(var j = 1; j < part.length/2; j++) { 
     ctx.lineTo(part[2*j], part[2*j+1]); 
    } 
    } 
}; 

//combine the above two functions to actually draw a dashed curve. 
var drawDashedBezier = function(ctx, controlPoints, dashPattern) { 
    var dashes = calculateDashedBezier(controlPoints, dashPattern); 
    ctx.beginPath(); 
    ctx.strokeStyle = /* ... */ 
    ctx.lineWidth = /* ... */ 
    pathParts(ctx, dashes); 
    ctx.stroke(); 
}; 

这种方法的主要问题是其愚蠢的粒度。当步长对于(小)破折号或(大)曲线来说太大时,步长将无法正常工作,并且破折号边界不会完全落在您想要的位置。当步长太小时,最终可能会在距离彼此相距子像素距离的点上做lineTo() s,有时会造成AA伪影。过滤掉子像素距离坐标并不困难,但是生成比您真正需要的更多“顶点”效率不高。实际上,迈出更好的步长实际上是我认为更多的分析攻击。

使用此方法有一个好处:如果将bezier(controlPoints, t)替换为其他任何曲线评估值,您将绘制虚线的开度! - 同样会出现前面段落中列出的相同潜在问题。但是对粒度问题的一个非常好的解决方案可以适用于所有'表现良好'的曲线。

+1

你正确地认识到,假设步骤= 0.001是一个很大的风险,因为你可能不知道高级贝塞尔的大小。如果通过找到中点直到两点的距离变为零或曲线变直,递归计算步骤会更好...... –

+0

另一种更简单的方法是将“step”设置为等于弧长的近似值的贝塞尔曲线除以最短的短划线长度。不过,如果你的曲线是“1/step”比弧长除以最小虚线长度大得多,那么固定的“step”将工作得很好。 – ellisbben

+0

有帮助,谢谢... –