Skip to content

Commit

Permalink
adaptive division for bezierCubicToQuad fontello#25
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyapro committed Jul 22, 2014
1 parent 86a4a03 commit 5b4cc61
Showing 1 changed file with 48 additions and 11 deletions.
59 changes: 48 additions & 11 deletions lib/math.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict';

var maxDefect = Math.sqrt(3) / 18;
var precision = 1.0;

function Point(x, y) {
this.x = x;
this.y = y;
Expand Down Expand Up @@ -30,12 +33,15 @@ Point.prototype.sqr = function () {
};



// converts cubic bezier to quad
function toQuad(p1, c1, c2, p2) {
// Quad control point is (3*c2 - p2 + 3*c1 - p1)/4
return [p1, c2.mul(3).sub(p2).add(c1.mul(3)).sub(p1).div(4), p2];
}



/*
* Aproximates cubic curve to 2 quad curves. Returns array of quad curves
*
Expand All @@ -47,17 +53,7 @@ function toQuad(p1, c1, c2, p2) {
*
* (!) We could use more slices, but FONT SIZE DOES MATTER !!!
*/
function bezierCubicToQuad(p1, c1, c2, p2) {

// check first, if we can aproximate with quad directly
// |p2 - 3*c2 + 3*c1 - p1|/2 should be small (zero in ideal)
// http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
var cpDistance = p2.sub(c2.mul(3)).add(c1.mul(3)).sub(p1).dist()/2;

if (cpDistance <= 3) {
return [ toQuad(p1, c1, c2, p2) ];
}

function midpointSplit(p1, c1, c2, p2){
// Split to 2 qubic beziers
// https://www.atalasoft.com/blogs/stevehawley/may-2013/how-to-split-a-cubic-bezier-curve
// http://www.timotheegroleau.com/Flash/articles/cubic_bezier/bezier_lib.as
Expand All @@ -71,9 +67,50 @@ function bezierCubicToQuad(p1, c1, c2, p2) {

// now replace each half with quad curve
return [ q1, q2 ];
}



/*
* Adaptive division:
* consider a division point at the maximum value of t for which the first cubic segment
* can still be approximated by a quadratic within the required precision;
* repeat the previous step for the remaining of the cubic.
*/
function bezierCubicToQuad(p1, c1, c2, p2){
// check first, if we can aproximate with quad directly
// |p2 - 3*c2 + 3*c1 - p1|/2 should be small (zero in ideal)
// http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
var cpDistance = p2.sub(c2.mul(3)).add(c1.mul(3)).sub(p1).dist()/2;

if (cpDistance <= 3) {
return [ toQuad(p1, c1, c2, p2) ];
}

//compute maximum t for which the first cubic segment can still be approximated by a quadratic
// within the required precision
var t = Math.pow(precision / (cpDistance * maxDefect), 1/3);

if (t < 1) {
// detaching segment of cubic: p1 s11 s12 s3 - first, s3 s21 s22 p2 - second
var ct = c2.sub(c1).mul(t).add(c1);
var s11 = c1.sub(p1).mul(t).add(p1);
var s12 = ct.sub(s11).mul(t).add(s11);
var s22 = p2.sub(c2).mul(t).add(c2);
var s21 = s22.sub(ct).mul(t).add(ct);
var s3 = s21.sub(s12).mul(t).add(s12);

//midpointSplit for first segment and recursive adaptiveDivision for second
return midpointSplit(p1, s11, s12, s3)
.concat(bezierCubicToQuad(s3, s21, s22, p2));
}

// if t is greater than 1.0, the cubic Bezier can be directly approximated by the
// mid-point approximation within the specified tolerance
return midpointSplit(p1, c1, c2, p2);
}


/*
* Check if 3 points are in line, and second in the midle.
* Used to replace quad curves with lines or join lines
Expand Down

0 comments on commit 5b4cc61

Please sign in to comment.