Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I would presume that JavaFX is using a general technique for drawing any dashed curve and just happens to be using it on a bezier in that example.</p> <p>The hard part is figuring out where to start and stop each dash, which requires knowing the <a href="http://en.wikipedia.org/wiki/Arc_length" rel="nofollow">arc length</a> of your bezier curve at various points along it.</p> <p>There is an analytic approach, but I would suggest the following:</p> <pre><code>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 &lt;= 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 &gt;= 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 &gt; 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 &lt; pathParts.length; i++) { var part = pathParts[i]; if(part.length &gt; 0) ctx.moveTo(part[0], part[1]); for(var j = 1; j &lt; 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(); }; </code></pre> <p>The main problem with this approach is its unintelligent granularity. When step is too big for your (small) dashes or (big) curve, the step size will not work well and dash boundaries will not fall exactly where you want them to. When step is too small, you may end up doing <code>lineTo()</code>s on points that are a sub-pixel distance away from each other, making for AA artifacts sometimes. Filtering out sub-pixel distance coordinates is not hard, but it is inefficient to generate more 'vertices' than you really need. Coming up with a better step size is actually something I'd consider attacking more analytically.</p> <p>There is one bonus to using this approach: if you replace <code>bezier(controlPoints, t)</code> with anything else that evaluates to a curve, you'll be drawing dashed whatevers!-- again with the same potential problems listed in the previous paragraph. But a really good solution to the granularity problem could work for all 'well-behaved' curves.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload