diff --git a/generator.js b/generator.js index 73d7319..dec33f4 100644 --- a/generator.js +++ b/generator.js @@ -3,22 +3,22 @@ var path = require('path'); var TTFWriter = require('node-sfnt').TTFWriter; var argv = require('yargs').argv; -var buildGlyphs = require('./buildglyphs.js'); +var buildGlyphs = require('./buildglyphs.js'); var parameters = require('./parameters'); var toml = require('toml'); var Glyph = require('./support/glyph'); -function hasv(obj){ - if(!obj) return false; - for(var k in obj) if(obj[k]) return true; +function hasv(obj) { + if (!obj) return false; + for (var k in obj) if (obj[k]) return true; return false; } // Font building -var font = function(){ +var font = function() { var parametersData = toml.parse(fs.readFileSync(path.join(path.dirname(require.main.filename), 'parameters.toml'), 'utf-8')); - var emptyFont = toml.parse(fs.readFileSync(path.join(path.dirname(require.main.filename), 'emptyfont.toml'), 'utf-8')); + var emptyFont = toml.parse(fs.readFileSync(path.join(path.dirname(require.main.filename), 'emptyfont.toml'), 'utf-8')); var para = parameters.build(parametersData, argv._); var fontUniqueName = para.family + ' ' + para.style + ' ' + para.version + ' (' + para.codename + ')' @@ -28,11 +28,11 @@ var font = function(){ font.features.sscompose = para.sscompose; font.parameters = para; return font; -}(); +} (); -if(argv.charmap) (function(){ +if (argv.charmap) (function() { console.log(' Writing character map -> ' + argv.charmap); - fs.writeFileSync(argv.charmap, JSON.stringify(font.glyf.map(function(glyph){ + fs.writeFileSync(argv.charmap, JSON.stringify(font.glyf.map(function(glyph) { return [ glyph.name, glyph.unicode, @@ -41,13 +41,13 @@ if(argv.charmap) (function(){ })), 'utf8') })(); -if(argv.feature) (function(){ +if (argv.feature) (function() { console.log(' Writing feature file -> ' + argv.feature); var featurefile = '\n\n'; // markGlyphs - for(var feature in font.features.cv) { + for (var feature in font.features.cv) { var base = [], replace = []; - for(var key in font.features.cv[feature]) { + for (var key in font.features.cv[feature]) { base.push(key); replace.push(font.features.cv[feature][key]); }; @@ -55,37 +55,37 @@ if(argv.feature) (function(){ featurefile += 'lookup ' + lookupName + ' { sub [' + base.join(' ') + '] by [' + replace.join(' ') + '];} ' + lookupName + ';\n\n'; featurefile += 'feature ' + feature + ' { script latn; lookup ' + lookupName + '; script grek; lookup ' + lookupName + '; script cyrl; lookup ' + lookupName + '; script DFLT; lookup ' + lookupName + '; } ' + feature + ';\n\n'; } - for(var feature in font.features.sscompose) { - var stmt = font.features.sscompose[feature].map(function(lookup){ return 'lookup ' + lookup + 'Auto'}).join(';') + for (var feature in font.features.sscompose) { + var stmt = font.features.sscompose[feature].map(function(lookup) { return 'lookup ' + lookup + 'Auto' }).join(';') featurefile += 'feature ' + feature + ' { script latn; ' + stmt + '; script grek; ' + stmt + '; script cyrl; ' + stmt + '; script DFLT; ' + stmt + '; } ' + feature + ';\n\n'; } - for(var key in font.features.markGlyphs){ + for (var key in font.features.markGlyphs) { featurefile += '@MG_' + key + '= [' + font.features.markGlyphs[key].join(' ') + '];\n' } // mark var mark = font.features.mark; - for(var id in mark) { + for (var id in mark) { var lookup = mark[id]; var lookupName = 'markAuto_' + id; featurefile += 'lookup ' + lookupName + ' {' + lookup.marks.join(';\n') + ';\n' + lookup.bases.join(';\n') + ';} ' + lookupName + ';' } - + // mkmk var mkmk = font.features.mkmk; featurefile += 'lookup mkmkAuto {' + mkmk.marks.join(';\n') + ';\n' + mkmk.bases.join(';\n') + ';} mkmkAuto;' - + // gdef var gdef = font.features.gdef; featurefile += '@GDEF_Simple = [' + gdef.simple.join(' \n') + '];\n' + '@GDEF_Ligature =[' + gdef.ligature.join(' \n') + '];\n' + '@GDEF_Mark = [' + gdef.mark.join(' \n') + '];\n' + 'table GDEF { GlyphClassDef @GDEF_Simple, @GDEF_Ligature, @GDEF_Mark, ;} GDEF;' - + featurefile += fs.readFileSync(__dirname + '/features/common.fea', 'utf-8'); featurefile += fs.readFileSync(__dirname + '/features/' + (font.parameters.isItalic ? 'italiconly.fea' : 'uprightonly.fea'), 'utf-8'); - if(!font.parameters.disableLigation) + if (!font.parameters.disableLigation) featurefile += fs.readFileSync(__dirname + '/features/ligation.fea', 'utf-8'); fs.writeFileSync(argv.feature, featurefile, 'utf-8'); })(); @@ -136,14 +136,14 @@ if(argv.ttf) (function(){ })(); */ -if(argv.svg) (function(){ +if (argv.svg) (function() { console.log(' Writing outline as SVG -> ' + argv.svg); - + var foundNaN = false; var glyfname = ''; function cov(x) { - if(!isFinite(x)){ - if(!foundNaN) { + if (!isFinite(x)) { + if (!foundNaN) { console.log("*** NaN value found in " + argv.svg + '(' + glyfname + ')' + " ***") foundNaN = true } @@ -151,33 +151,33 @@ if(argv.svg) (function(){ } return Math.round(x * 10000) / 10000 }; - function mix(a, b, p){ return a + (b - a) * p }; - - function toSVGPath(glyph){ + function mix(a, b, p) { return a + (b - a) * p }; + + function toSVGPath(glyph) { var buf = ''; foundNaN = false; glyfname = glyph.name; - if(glyph.contours) for(var j = 0; j < glyph.contours.length; j++) { + if (glyph.contours) for (var j = 0; j < glyph.contours.length; j++) { var contour = glyph.contours[j]; var lx = 0; var ly = 0; - if(contour.length) { + if (contour.length) { lx = contour[0].x; ly = contour[0].y; buf += 'M' + cov(lx) + ' ' + cov(ly); - for(var k = 1; k < contour.length; k++) if(contour[k].onCurve){ + for (var k = 1; k < contour.length; k++) if (contour[k].onCurve) { lx = contour[k].x; ly = contour[k].y; buf += 'L' + cov(lx) + ' ' + cov(ly); - } else if(contour[k].cubic) { + } else if (contour[k].cubic) { var rx = contour[k + 2].x; var ry = contour[k + 2].y; buf += 'C' + [contour[k].x, contour[k].y, contour[k + 1].x, contour[k + 1].y, rx, ry].map(cov).join(' '); lx = rx; ly = ry; k += 2; - } else if(contour[k + 1]) { - if(contour[k + 1].onCurve){ + } else if (contour[k + 1]) { + if (contour[k + 1].onCurve) { var rx = contour[k + 1].x; var ry = contour[k + 1].y; } else { @@ -188,11 +188,11 @@ if(argv.svg) (function(){ var y1 = mix(ly, contour[k].y, 2 / 3); var x2 = mix(rx, contour[k].x, 2 / 3); var y2 = mix(ry, contour[k].y, 2 / 3); - + buf += 'C' + [cov(x1), cov(y1), cov(x2), cov(y2), cov(rx), cov(ry)].join(' '); lx = rx; ly = ry; - if(contour[k + 1].onCurve) k += 1; + if (contour[k + 1].onCurve) k += 1; } else { var rx = contour[0].x; var ry = contour[0].y; @@ -206,18 +206,18 @@ if(argv.svg) (function(){ return buf; } var svg = '' - + var skew = (argv.uprightify ? 1 : 0) * Math.tan((font.post.italicAngle || 0) / 180 * Math.PI); var scale = (argv.upm || 1000) / 1000; - + svg += '' - - for(var j = 0; j < font.glyf.length; j++){ + + for (var j = 0; j < font.glyf.length; j++) { var g = font.glyf[j]; - if(g.contours) { - for(var k = 0; k < g.contours.length; k++) { + if (g.contours) { + for (var k = 0; k < g.contours.length; k++) { var contour = g.contours[k]; - for(var p = 0; p < contour.length; p++) { + for (var p = 0; p < contour.length; p++) { contour[p].x += contour[p].y * skew; contour[p].x *= scale; contour[p].y *= scale; @@ -226,20 +226,20 @@ if(argv.svg) (function(){ g.advanceWidth *= scale; //Glyph.prototype.cleanup.call(g, 0.25); } - var gd = '<' + (j === 0 ? 'missing-glyph' : 'glyph') + ' glyph-name="' + g.name + '" horiz-adv-x="' + g.advanceWidth + '" '+ (g.unicode && g.unicode.length ? 'unicode="&#x' + g.unicode[0].toString(16) + ';"' : '') +' d="' + toSVGPath(g) + '" />' + var gd = '<' + (j === 0 ? 'missing-glyph' : 'glyph') + ' glyph-name="' + g.name + '" horiz-adv-x="' + g.advanceWidth + '" ' + (g.unicode && g.unicode.length ? 'unicode="&#x' + g.unicode[0].toString(16) + ';"' : '') + ' d="' + toSVGPath(g) + '" />' svg += gd; } svg += '' fs.writeFileSync(argv.svg, svg, 'utf-8') })(); -if(argv.meta) (function(){ +if (argv.meta) (function() { console.log(' Writing metadata as JSON -> ' + argv.meta); - if(argv.svg){ + if (argv.svg) { font.glyf = null; font.glyfMap = null; } - if(argv.feature){ + if (argv.feature) { font.features = null; } fs.writeFileSync(argv.meta, JSON.stringify(font)); diff --git a/pass2-finalize.js b/pass2-finalize.js index 1a43f1d..69ed1a1 100644 --- a/pass2-finalize.js +++ b/pass2-finalize.js @@ -8,35 +8,35 @@ var argv = require('yargs').argv; var param = toml.parse(fs.readFileSync(path.join(path.dirname(require.main.filename), 'parameters.toml'), 'utf-8')) function toArrayBuffer(buffer) { - var length = buffer.length; - var view = new DataView(new ArrayBuffer(length), 0, length); - for (var i = 0, l = length; i < l; i++) { - view.setUint8(i, buffer[i], false); - } - return view.buffer; + var length = buffer.length; + var view = new DataView(new ArrayBuffer(length), 0, length); + for (var i = 0, l = length; i < l; i++) { + view.setUint8(i, buffer[i], false); + } + return view.buffer; } function toBuffer(arrayBuffer) { - var length = arrayBuffer.byteLength; - var view = new DataView(arrayBuffer, 0, length); - var buffer = new Buffer(length); - for (var i = 0, l = length; i < l; i++) { - buffer[i] = view.getUint8(i, false); - } - return buffer; + var length = arrayBuffer.byteLength; + var view = new DataView(arrayBuffer, 0, length); + var buffer = new Buffer(length); + for (var i = 0, l = length; i < l; i++) { + buffer[i] = view.getUint8(i, false); + } + return buffer; } -var options = {preserveOS2Version: true, preserveXAvgCharWidth: true, writeUnknownTables: true, hinting: true} +var options = { preserveOS2Version: true, preserveXAvgCharWidth: true, writeUnknownTables: true, hinting: true } function readttf(file) { - var data = fs.readFileSync(file); - var buffer = toArrayBuffer(data); - var ttf = (new TTFReader(options)).read(buffer); - return ttf; + var data = fs.readFileSync(file); + var buffer = toArrayBuffer(data); + var ttf = (new TTFReader(options)).read(buffer); + return ttf; } -function writettf(ttf, file){ - var buffer = new TTFWriter(options).write(ttf); - fs.writeFileSync(file, toBuffer(buffer)); +function writettf(ttf, file) { + var buffer = new TTFWriter(options).write(ttf); + fs.writeFileSync(file, toBuffer(buffer)); } var glyfsource = readttf(argv._[0]); @@ -44,8 +44,8 @@ var ttf = JSON.parse(fs.readFileSync(argv._[1], 'utf-8')); ttf.post.format = 3 ttf.DSIG = { // add a dummy SDIG - name: 'DSIG', - content: [0, 0, 0, 1, 0, 0, 0, 0] + name: 'DSIG', + content: [0, 0, 0, 1, 0, 0, 0, 0] } ttf.glyf = glyfsource.glyf; ttf.GDEF = glyfsource.GDEF; diff --git a/support/fairify.js b/support/fairify.js index 190fcdb..c77b08f 100644 --- a/support/fairify.js +++ b/support/fairify.js @@ -8,40 +8,40 @@ var CROWD = 4; var LOOSE = 6; var SMALL = 1e-4 var CLOSE = 1; -function cross(z1, z2, z3){ +function cross(z1, z2, z3) { return (z2.x - z1.x) * (z3.y - z1.y) - (z3.x - z1.x) * (z2.y - z1.y) }; -function dot(z1, z2, z3){ +function dot(z1, z2, z3) { return (z2.x - z1.x) * (z3.x - z1.x) + (z3.y - z1.y) * (z2.y - z1.y) }; -function solveTS(a, b, c, out, flag){ +function solveTS(a, b, c, out, flag) { var delta = b * b - 4 * a * c; - if(delta > 0){ + if (delta > 0) { var t1 = (Math.sqrt(delta) - b) / (2 * a) var t2 = (-Math.sqrt(delta) - b) / (2 * a) - if(flag){ - if(t1 >= 0 && t1 <= 1) out.push(t1) - if(t2 >= 0 && t2 <= 1) out.push(t2) + if (flag) { + if (t1 >= 0 && t1 <= 1) out.push(t1) + if (t2 >= 0 && t2 <= 1) out.push(t2) } else { - if(t1 > 0 && t1 < 1) out.push(t1) - if(t2 > 0 && t2 < 1) out.push(t2) + if (t1 > 0 && t1 < 1) out.push(t1) + if (t2 > 0 && t2 < 1) out.push(t2) } - } else if (delta === 0){ + } else if (delta === 0) { var t = -b / (2 * a) - if(flag){ - if(t >= 0 && t <= 1) out.push(t) + if (flag) { + if (t >= 0 && t <= 1) out.push(t) } else { - if(t > 0 && t < 1) out.push(t) + if (t > 0 && t < 1) out.push(t) } }; } -function findExtrema(z1, z2, z3, z4, out){ +function findExtrema(z1, z2, z3, z4, out) { var a = 3 * (-z1.y + 3 * z2.y - 3 * z3.y + z4.y); var b = 6 * (z1.y - 2 * z2.y + z3.y); var c = 3 * (z2.y - z1.y); solveTS(a, b, c, out); }; -function findInflections(z1, z2, z3, z4, out){ +function findInflections(z1, z2, z3, z4, out) { var ax = z2.x - z1.x; var ay = z2.y - z1.y; var bx = z3.x - z2.x - ax; @@ -50,18 +50,18 @@ function findInflections(z1, z2, z3, z4, out){ var cy = z4.y - z3.y - ay - 2 * by; solveTS(bx * cy - by * cx, ax * cy - ay * cx, ax * by - ay * bx, out, true) } -function rotate(z, angle){ +function rotate(z, angle) { var c = Math.cos(angle), s = Math.sin(angle); return { x: c * z.x + s * z.y, - y : -s * z.x + c * z.y + y: -s * z.x + c * z.y } }; -function ASCEND(a, b){ return a - b } -function fineAllExtremae(z1, z2, z3, z4, angles){ +function ASCEND(a, b) { return a - b } +function fineAllExtremae(z1, z2, z3, z4, angles) { var exs = []; findInflections(z1, z2, z3, z4, exs); - for(var a = 0; a < angles; a += 1){ + for (var a = 0; a < angles; a += 1) { findExtrema(z1, z2, z3, z4, exs); z1 = rotate(z1, Math.PI / angles); z2 = rotate(z2, Math.PI / angles); @@ -70,145 +70,145 @@ function fineAllExtremae(z1, z2, z3, z4, angles){ } return exs.sort(ASCEND) }; -function mix(z1, z2, t){ - return {x: (1 - t) * z1.x + t * z2.x, y: (1 - t) * z1.y + t * z2.y} +function mix(z1, z2, t) { + return { x: (1 - t) * z1.x + t * z2.x, y: (1 - t) * z1.y + t * z2.y } }; -function bez2(z1, z2, z3, t){ +function bez2(z1, z2, z3, t) { return { x: (1 - t) * (1 - t) * z1.x + 2 * (1 - t) * t * z2.x + t * t * z3.x, y: (1 - t) * (1 - t) * z1.y + 2 * (1 - t) * t * z2.y + t * t * z3.y } }; -function bez3(z1, z2, z3, z4, t){ +function bez3(z1, z2, z3, z4, t) { return { x: (1 - t) * (1 - t) * (1 - t) * z1.x - + 3 * t * (1 - t) * (1 - t) * z2.x - + 3 * t * t * (1 - t) * z3.x - + t * t * t *z4.x, + + 3 * t * (1 - t) * (1 - t) * z2.x + + 3 * t * t * (1 - t) * z3.x + + t * t * t * z4.x, y: (1 - t) * (1 - t) * (1 - t) * z1.y - + 3 * t * (1 - t) * (1 - t) * z2.y - + 3 * t * t * (1 - t) * z3.y - + t * t * t *z4.y + + 3 * t * (1 - t) * (1 - t) * z2.y + + 3 * t * t * (1 - t) * z3.y + + t * t * t * z4.y } }; -function splitBefore(z1, z2, z3, z4, t){ +function splitBefore(z1, z2, z3, z4, t) { return [z1, mix(z1, z2, t), bez2(z1, z2, z3, t), bez3(z1, z2, z3, z4, t)] }; -function splitAfter(z1, z2, z3, z4, t){ +function splitAfter(z1, z2, z3, z4, t) { return [bez3(z1, z2, z3, z4, t), bez2(z2, z3, z4, t), mix(z3, z4, t), z4] }; -function splitAtExtremae(z1, z2, z3, z4, angles, splitpoints){ +function splitAtExtremae(z1, z2, z3, z4, angles, splitpoints) { var ts = fineAllExtremae(z1, z2, z3, z4, angles); - if(ts[0] < SMALL) { + if (ts[0] < SMALL) { ts[0] = 0; } else { ts.unshift(0); } - if(ts[ts.length - 1] > 1 - SMALL) { + if (ts[ts.length - 1] > 1 - SMALL) { ts[ts.length - 1] = 1; } else { ts.push(1); }; - for(var k = 0; k < ts.length; k++){ - if(k > 0){ + for (var k = 0; k < ts.length; k++) { + if (k > 0) { var t1 = ts[k - 1]; var t2 = ts[k]; var bef = splitBefore(z1, z2, z3, z4, t2); var seg = splitAfter(bef[0], bef[1], bef[2], bef[3], t1 / t2); seg[1].onCurve = seg[2].onCurve = false; seg[1].cubic = seg[2].cubic = true; seg[3].onCurve = true; - splitpoints.push(seg[1],seg[2],seg[3]); + splitpoints.push(seg[1], seg[2], seg[3]); } }; }; -function splitSegment(z1, z2, z3, z4, angles, splitpoints){ +function splitSegment(z1, z2, z3, z4, angles, splitpoints) { var ts = []; var inflectAtEnd = false; findInflections(z1, z2, z3, z4, ts); ts = ts.sort(ASCEND); - if(ts[0] < SMALL) { + if (ts[0] < SMALL) { ts[0] = 0; splitpoints[splitpoints.length - 1].inflect = true; } else { ts.unshift(0); } - if(ts[ts.length - 1] > 1 - SMALL) { + if (ts[ts.length - 1] > 1 - SMALL) { inflectAtEnd = true; ts[ts.length - 1] = 1; } else { ts.push(1); }; - for(var k = 0; k < ts.length; k++){ - if(k > 0) { + for (var k = 0; k < ts.length; k++) { + if (k > 0) { var t1 = ts[k - 1]; var t2 = ts[k]; var bef = splitBefore(z1, z2, z3, z4, t2); var seg = splitAfter(bef[0], bef[1], bef[2], bef[3], t1 / t2); splitAtExtremae(seg[0], seg[1], seg[2], seg[3], angles, splitpoints); - if(t2 < 1 || inflectAtEnd) splitpoints[splitpoints.length - 1].inflect = true; + if (t2 < 1 || inflectAtEnd) splitpoints[splitpoints.length - 1].inflect = true; } }; }; -function fitpts(p1, c1, c2, p2){ - var d1 = {x : c1.x - p1.x, y: c1.y - p1.y} - var d2 = {x : c2.x - p2.x, y: c2.y - p2.y} - +function fitpts(p1, c1, c2, p2) { + var d1 = { x: c1.x - p1.x, y: c1.y - p1.y } + var d2 = { x: c2.x - p2.x, y: c2.y - p2.y } + var det = d2.x * d1.y - d2.y * d1.x; - if(Math.abs(det) < 1e-6) return null; + if (Math.abs(det) < 1e-6) return null; var u = ((p2.y - p1.y) * d2.x - (p2.x - p1.x) * d2.y) / det var v = ((p2.y - p1.y) * d1.x - (p2.x - p1.x) * d1.y) / det - if(u <= 0 || v <= 0) return null; + if (u <= 0 || v <= 0) return null; var mid = { x: p1.x + d1.x * u, y: p1.y + d1.y * u } - return [mix(mid, p1, 1/3), mix(mid, p2, 1/3)] + return [mix(mid, p1, 1 / 3), mix(mid, p2, 1 / 3)] }; -function distance(z1, z2){ +function distance(z1, z2) { return Math.sqrt((z1.x - z2.x) * (z1.x - z2.x) + (z1.y - z2.y) * (z1.y - z2.y)) } -function veryclose(z1, z2){ +function veryclose(z1, z2) { return (z1.x - z2.x) * (z1.x - z2.x) + (z1.y - z2.y) * (z1.y - z2.y) <= SMALL }; -function angleBetween(z1, z2, z3, z4){ +function angleBetween(z1, z2, z3, z4) { return (Math.atan2(z2.y - z1.y, z2.x - z1.x) - Math.atan2(z4.y - z3.y, z4.x - z3.x)) % Math.PI; }; -function pldistance(z1, z2, z){ +function pldistance(z1, z2, z) { return Math.abs((z2.y - z1.y) * z.x - (z2.x - z1.x) * z.y + z2.x * z1.y - z2.y * z1.x) / Math.sqrt((z2.x - z1.x) * (z2.x - z1.x) + (z2.y - z1.y) * (z2.y - z1.y)) } -function estimateSegments(z1, z2){ +function estimateSegments(z1, z2) { var hspan = Math.abs(z1.x - z2.x); var vspan = Math.abs(z1.y - z2.y); - return hspan <= 5 * CROWD || vspan <= 5 * CROWD ? VERYCROWD : hspan <= 10 * LOOSE || vspan <= 10 * LOOSE ? CROWD : LOOSE; + return hspan <= 5 * CROWD || vspan <= 5 * CROWD ? VERYCROWD : hspan <= 10 * LOOSE || vspan <= 10 * LOOSE ? CROWD : LOOSE; }; -function enoughRotate(bef, z0, z1, z2, aft, cleanMore, flagl, flagr){ +function enoughRotate(bef, z0, z1, z2, aft, cleanMore, flagl, flagr) { var angleRotatedBefore = Math.abs(angleBetween(bef.next || z1, bef, z1, z0)); var angleRotatedAfter = Math.abs(angleBetween(aft.prev || z1, aft, z1, z2)); var smallanglel = (cleanMore ? SMALLANGLE_CLEANMORE : SMALLANGLE) * (flagl && cleanMore ? 1 : 2); var smallangler = (cleanMore ? SMALLANGLE_CLEANMORE : SMALLANGLE) * (flagr && cleanMore ? 1 : 2); return !((angleRotatedBefore < smallanglel || angleRotatedBefore > Math.PI - smallanglel) && pldistance(z1, z0, bef) <= CLOSE && distance(z1, bef) <= 5 * CROWD - || (flagr ? false : (angleRotatedAfter < smallangler || angleRotatedAfter > Math.PI - smallangler) && pldistance(z1, z2, aft) <= CLOSE && distance(z1, aft) <= 5 * CROWD)) + || (flagr ? false : (angleRotatedAfter < smallangler || angleRotatedAfter > Math.PI - smallangler) && pldistance(z1, z2, aft) <= CLOSE && distance(z1, aft) <= 5 * CROWD)) }; -function fairify(scurve, gizmo, denseQ, cleanMore){ - for(var j = 0; j < scurve.length; j++){ +function fairify(scurve, gizmo, denseQ, cleanMore) { + for (var j = 0; j < scurve.length; j++) { scurve[j] = Transform.untransform(gizmo, scurve[j]) } var splitpoints = [scurve[0]]; var last = scurve[0]; - for(var j = 1; j < scurve.length; j++){ - if(scurve[j].onCurve){ + for (var j = 1; j < scurve.length; j++) { + if (scurve[j].onCurve) { splitpoints.push(last = scurve[j]) - } else if(scurve[j].cubic) { + } else if (scurve[j].cubic) { var z1 = last, z2 = scurve[j], z3 = scurve[j + 1], z4 = scurve[j + 2]; - if(!(veryclose(z1, z2) && veryclose(z2, z3) && veryclose(z3, z4))) { + if (!(veryclose(z1, z2) && veryclose(z2, z3) && veryclose(z3, z4))) { splitSegment(z1, z2, z3, z4, ANGLES, splitpoints); last = z4; } j += 2; } else { var z1 = last, zm = scurve[j], z4 = scurve[j + 1] - if(!(veryclose(z1, zm) && veryclose(zm, z4))) { - var z2 = mix(zm, z1, 1/3); - var z3 = mix(zm, z4, 1/3); + if (!(veryclose(z1, zm) && veryclose(zm, z4))) { + var z2 = mix(zm, z1, 1 / 3); + var z3 = mix(zm, z4, 1 / 3); splitSegment(z1, z2, z3, z4, ANGLES, splitpoints); last = z4; } @@ -216,31 +216,31 @@ function fairify(scurve, gizmo, denseQ, cleanMore){ } }; // Mark corners and extremae - for(var j = 1; j < splitpoints.length - 1; j++) { - if(splitpoints[j].onCurve && !splitpoints[j - 1].onCurve){ + for (var j = 1; j < splitpoints.length - 1; j++) { + if (splitpoints[j].onCurve && !splitpoints[j - 1].onCurve) { splitpoints[j].prev = splitpoints[j - 1] } - if(splitpoints[j].onCurve && !splitpoints[j + 1].onCurve){ + if (splitpoints[j].onCurve && !splitpoints[j + 1].onCurve) { splitpoints[j].next = splitpoints[j + 1] } - if(splitpoints[j].onCurve && !splitpoints[j - 1].onCurve && !splitpoints[j + 1].onCurve){ + if (splitpoints[j].onCurve && !splitpoints[j - 1].onCurve && !splitpoints[j + 1].onCurve) { var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1] - if(cross(z1, z0, z2) < 1e-6 && dot(z1, z0, z2) < 0){ + if (cross(z1, z0, z2) < 1e-6 && dot(z1, z0, z2) < 0) { var angle0 = Math.atan2(z2.y - z0.y, z2.x - z0.x); var angle = Math.abs(angle0 / Math.PI * 2 % 1); - if(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL){ + if (Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL) { z1.mark = true; // extrema z1.inflect = false; } else { var isInflection = false; - if(j > 2 && j < splitpoints.length - 2){ + if (j > 2 && j < splitpoints.length - 2) { var za = bez3(z1, z0, splitpoints[j - 2], splitpoints[j - 3], SMALL); var zb = bez3(z1, z2, splitpoints[j + 2], splitpoints[j + 3], SMALL); - var inflect = ((z0.y-z2.y)*(za.x-z0.x) + (z2.x-z0.x)*(za.y-z0.y)) - * ((z0.y-z2.y)*(zb.x-z0.x) + (z2.x-z0.x)*(zb.y-z0.y)); - if(inflect < 0) isInflection = true; + var inflect = ((z0.y - z2.y) * (za.x - z0.x) + (z2.x - z0.x) * (za.y - z0.y)) + * ((z0.y - z2.y) * (zb.x - z0.x) + (z2.x - z0.x) * (zb.y - z0.y)); + if (inflect < 0) isInflection = true; }; - if(z1.inflect || isInflection) { + if (z1.inflect || isInflection) { z1.mark = true; z1.asinflect = true; } @@ -248,59 +248,59 @@ function fairify(scurve, gizmo, denseQ, cleanMore){ } else { z1.mark = true; // also corner } - } else if(splitpoints[j].onCurve) { + } else if (splitpoints[j].onCurve) { splitpoints[j].mark = true // corner } }; splitpoints[0].mark = splitpoints[splitpoints.length - 1].mark = true; // Mark cleanup inflections - for(var pass = 0; pass < 2; pass++){ - for(var j = 1; j < splitpoints.length - 1; j++) { - if(splitpoints[j].mark){ - for(var k = j - 1; k >= 0 && !splitpoints[k].mark; k--); + for (var pass = 0; pass < 2; pass++) { + for (var j = 1; j < splitpoints.length - 1; j++) { + if (splitpoints[j].mark) { + for (var k = j - 1; k >= 0 && !splitpoints[k].mark; k--); lastmark = splitpoints[k]; - for(var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++); + for (var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++); nextmark = splitpoints[k]; } - if(splitpoints[j].mark && splitpoints[j].asinflect){ + if (splitpoints[j].mark && splitpoints[j].asinflect) { var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1] - if(!(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark, cleanMore, lastmark.asinflect, nextmark.asinflect))) { + if (!(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark, cleanMore, lastmark.asinflect, nextmark.asinflect))) { //z1.mark = false; z0.remove = z1.remove = z2.remove = true; } } }; - for(var j = 0; j < splitpoints.length; j++) if(splitpoints[j].remove) splitpoints[j].mark = false; + for (var j = 0; j < splitpoints.length; j++) if (splitpoints[j].remove) splitpoints[j].mark = false; }; // Mark diagonals var lastmark = splitpoints[0]; - for(var k = 1; k < splitpoints.length && !splitpoints[k].mark; k++); + for (var k = 1; k < splitpoints.length && !splitpoints[k].mark; k++); var nextmark = splitpoints[k]; var segments = estimateSegments(lastmark, nextmark); - for(var j = 1; j < splitpoints.length - 1; j++) { - if(splitpoints[j].mark){ + for (var j = 1; j < splitpoints.length - 1; j++) { + if (splitpoints[j].mark) { lastmark = splitpoints[j]; - for(var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++); + for (var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++); nextmark = splitpoints[k]; segments = estimateSegments(lastmark, nextmark); } - if(splitpoints[j].onCurve && !splitpoints[j].mark){ + if (splitpoints[j].onCurve && !splitpoints[j].mark) { var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1]; var angle0 = Math.atan2(z2.y - z0.y, z2.x - z0.x); var angle = Math.abs(angle0 / Math.PI * segments % 1); var angleRotatedBefore = Math.abs(angleBetween(z1, lastmark, z1, z0)); var angleRotatedAfter = Math.abs(angleBetween(z1, nextmark, z1, z2)); - if(!(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL) - || !(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark))){ + if (!(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL) + || !(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark))) { z1.remove = z0.remove = z2.remove = true; } } }; - for(var j = 0; j < splitpoints.length; j++) if(splitpoints[j].onCurve && !splitpoints[j].remove && splitpoints[j + 1] && !splitpoints[j + 1].onCurve){ - for(var k = j + 2; k < splitpoints.length && splitpoints[k].remove; k++); - if(k - j > 2){ + for (var j = 0; j < splitpoints.length; j++) if (splitpoints[j].onCurve && !splitpoints[j].remove && splitpoints[j + 1] && !splitpoints[j + 1].onCurve) { + for (var k = j + 2; k < splitpoints.length && splitpoints[k].remove; k++); + if (k - j > 2) { var zs = fitpts(splitpoints[j], splitpoints[j + 1], splitpoints[k], splitpoints[k + 1]); - if(zs){ + if (zs) { zs[0].onCurve = zs[1].onCurve = false; zs[0].cubic = zs[1].cubic = true; splitpoints[j + 1] = zs[0]; @@ -310,7 +310,7 @@ function fairify(scurve, gizmo, denseQ, cleanMore){ j = k; }; var ans = [] - for(var j = 0; j < splitpoints.length; j++) if(splitpoints[j] && !splitpoints[j].remove) { + for (var j = 0; j < splitpoints.length; j++) if (splitpoints[j] && !splitpoints[j].remove) { ans.push(Transform.transformPoint(gizmo, splitpoints[j])) } return ans diff --git a/support/monotonic-interpolate.js b/support/monotonic-interpolate.js index c44e4a5..1e16f5e 100644 --- a/support/monotonic-interpolate.js +++ b/support/monotonic-interpolate.js @@ -1,6 +1,6 @@ module.exports = function(xs, ys) { var i, length = xs.length; - + // Deal with length issues if (length != ys.length) { throw 'Need an equal count of xs and ys.'; } if (length === 0) { return function(x) { return 0; }; } @@ -10,7 +10,7 @@ module.exports = function(xs, ys) { var result = +ys[0]; return function(x) { return result; }; } - + // Rearrange xs and ys so that xs is sorted var indexes = []; for (i = 0; i < length; i++) { indexes.push(i); } @@ -20,53 +20,53 @@ module.exports = function(xs, ys) { xs = []; ys = []; // Impl: Unary plus properly converts values to numbers for (i = 0; i < length; i++) { xs.push(+oldXs[indexes[i]]); ys.push(+oldYs[indexes[i]]); } - + // Get consecutive differences and slopes var dys = [], dxs = [], ms = []; for (i = 0; i < length - 1; i++) { var dx = xs[i + 1] - xs[i], dy = ys[i + 1] - ys[i]; - dxs.push(dx); dys.push(dy); ms.push(dy/dx); + dxs.push(dx); dys.push(dy); ms.push(dy / dx); } - + // Get degree-1 coefficients var c1s = [ms[0]]; for (i = 0; i < dxs.length - 1; i++) { var m = ms[i], mNext = ms[i + 1]; - if (m*mNext <= 0) { + if (m * mNext <= 0) { c1s.push(0); } else { var dx = dxs[i], dxNext = dxs[i + 1], common = dx + dxNext; - c1s.push(3*common/((common + dxNext)/m + (common + dx)/mNext)); + c1s.push(3 * common / ((common + dxNext) / m + (common + dx) / mNext)); } } c1s.push(ms[ms.length - 1]); - + // Get degree-2 and degree-3 coefficients var c2s = [], c3s = []; for (i = 0; i < c1s.length - 1; i++) { - var c1 = c1s[i], m = ms[i], invDx = 1/dxs[i], common = c1 + c1s[i + 1] - m - m; - c2s.push((m - c1 - common)*invDx); c3s.push(common*invDx*invDx); + var c1 = c1s[i], m = ms[i], invDx = 1 / dxs[i], common = c1 + c1s[i + 1] - m - m; + c2s.push((m - c1 - common) * invDx); c3s.push(common * invDx * invDx); } - + // Return interpolant function return function(x) { // The rightmost point in the dataset should give an exact result var i = xs.length - 1; if (x == xs[i]) { return ys[i]; } - + // Search for the interval x is in, returning the corresponding y if x is one of the original xs var low = 0, mid, high = c3s.length - 1; while (low <= high) { - mid = Math.floor(0.5*(low + high)); + mid = Math.floor(0.5 * (low + high)); var xHere = xs[mid]; if (xHere < x) { low = mid + 1; } else if (xHere > x) { high = mid - 1; } else { return ys[mid]; } } i = Math.max(0, high); - + // Interpolate - var diff = x - xs[i], diffSq = diff*diff; - return ys[i] + c1s[i]*diff + c2s[i]*diffSq + c3s[i]*diff*diffSq; + var diff = x - xs[i], diffSq = diff * diff; + return ys[i] + c1s[i] * diff + c2s[i] * diffSq + c3s[i] * diff * diffSq; }; }; \ No newline at end of file