Skip to content

Commit

Permalink
Merge pull request #42 from a-rodin/master
Browse files Browse the repository at this point in the history
Fixed smoothness at the ends of interpolated cubic bezier. Close #25
  • Loading branch information
Vitaly Puzrin committed Oct 28, 2015
2 parents 49aa894 + 547eb12 commit 56b97c9
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 58 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
WIP
---

- Fixed smoothness at the ends of interpolated cubic beziers.


2.0.2 / 2015-08-23
------------------

Expand Down
10 changes: 9 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,20 @@ function svg2ttf(svgString, options) {

_.forEach(glyphs, function (glyph) {

// Calculate accuracy for cubicToQuad transformation
// For glyphs with height and width smaller than 500 use relative 0.06% accuracy,
// for larger glyphs use fixed accuracy 0.3.
var glyphSize = Math.max(glyph.width, glyph.height);
var accuracy = (glyphSize > 500) ? 0.3 : glyphSize * 0.0006;

//SVG transformations
var svgPath = new SvgPath(glyph.d)
.abs()
.unshort()
.unarc()
.iterate(svg.cubicToQuad);
.iterate(function(segment, index, x, y) {
return svg.cubicToQuad(segment, index, x, y, accuracy);
});
var sfntContours = svg.toSfntCoutours(svgPath);

// Add contours to SFNT font
Expand Down
46 changes: 0 additions & 46 deletions lib/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,51 +29,6 @@ Point.prototype.sqr = function () {
return this.x*this.x + this.y*this.y;
};


// 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
*
* 1. Split cubic bezier-> 2 cubic beziers, by midpoint
* 2. Replace each cubic bezier with quad bezier
*
* This is a simplified approach. It can be improved by adaptive splitting,
* but in real life that's not needed.
*
* (!) 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) ];
}

// 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

// midpoints of qubic bezier
// (p2 + 3*c2 + 3*c1 + p1)/8
var mp = p2.add(c2.mul(3)).add(c1.mul(3)).add(p1).div(8);

var q1 = toQuad(p1, p1.add(c1).div(2), p1.add(c2).add(c1.mul(2)).div(4), mp);
var q2 = toQuad(mp, p2.add(c1).add(c2.mul(2)).div(4), p2.add(c2).div(2), p2);

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

}

/*
* Check if 3 points are in line, and second in the midle.
* Used to replace quad curves with lines or join lines
Expand All @@ -96,5 +51,4 @@ function isInLine(p1, m, p2, accuracy) {
}

module.exports.Point = Point;
module.exports.bezierCubicToQuad = bezierCubicToQuad;
module.exports.isInLine = isInLine;
22 changes: 11 additions & 11 deletions lib/svg.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

var _ = require('lodash');
var cubic2quad = require('cubic2quad');
var DOMParser = require('xmldom').DOMParser;
var math = require('./math');
var ucs2 = require('./ucs2');

function getGlyph(glyphElem) {
Expand Down Expand Up @@ -151,20 +151,20 @@ function load(str) {
}


function cubicToQuad(segment, index, x, y) {
function cubicToQuad(segment, index, x, y, accuracy) {
if (segment[0] === 'C') {
var quadCurves = math.bezierCubicToQuad(
new math.Point(x, y),
new math.Point(segment[1], segment[2]),
new math.Point(segment[3], segment[4]),
new math.Point(segment[5], segment[6]),
0.3
var quadCurves = cubic2quad(
x, y,
segment[1], segment[2],
segment[3], segment[4],
segment[5], segment[6],
accuracy
);

var res = [];
_.forEach(quadCurves, function(curve) {
res.push(['Q', curve[1].x, curve[1].y, curve[2].x, curve[2].y]);
});
for (var i = 2; i < quadCurves.length; i += 4) {
res.push(['Q', quadCurves[i], quadCurves[i + 1], quadCurves[i + 2], quadCurves[i + 3]]);
}
return res;
}
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"license": "MIT",
"dependencies": {
"argparse": "^1.0.2",
"cubic2quad": "^1.0.0",
"lodash": "^3.6.0",
"microbuffer": "^1.0.0",
"svgpath": "~2.0.0",
Expand Down

0 comments on commit 56b97c9

Please sign in to comment.