diff --git a/client/glyph.js b/client/glyph.js index 528f0ed0..2aec9408 100644 --- a/client/glyph.js +++ b/client/glyph.js @@ -8,10 +8,13 @@ var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF', function change_glyph(method, glyph) { glyph = glyph || Session.get('glyph.data'); Meteor.call(method, glyph, function(error, data) { + data.d = Glyphs.get_svg_path(data); + data.manual = data.manual || {}; data.manual.bridges_added = data.manual.bridges_added || []; data.manual.bridges_removed = data.manual.bridges_removed || []; data.manual.verified = data.manual.verified || false; + Session.set('glyph.data', data); if (method != 'save_glyph') { Session.set('glyph.show_strokes', true); @@ -181,6 +184,7 @@ Template.glyph.helpers({ return !!Session.get('glyph.show_strokes'); }, strokes: function() { + return []; var glyph = Session.get('glyph.data'); var result = []; var strokes = glyph.extractor.strokes; diff --git a/lib/collections.js b/lib/collections.js deleted file mode 100644 index f21bf66e..00000000 --- a/lib/collections.js +++ /dev/null @@ -1 +0,0 @@ -Glyphs = new Mongo.Collection('glyphs'); diff --git a/lib/glyphs.js b/lib/glyphs.js new file mode 100644 index 00000000..e5a21c27 --- /dev/null +++ b/lib/glyphs.js @@ -0,0 +1,102 @@ +Glyphs = new Mongo.Collection('glyphs'); + +Glyphs.get_svg_path = function(glyph) { + var terms = []; + for (var i = 0; i < glyph.path.length; i++) { + var segment = glyph.path[i]; + terms.push(segment.type); + if (segment.x1 !== undefined) { + terms.push(segment.x1); + terms.push(segment.y1); + } + terms.push(segment.x); + terms.push(segment.y); + } + return terms.join(' '); +} + +// Error out if the condition does not hold. +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +function clone(point) { + return [point[0], point[1]]; +} + +// Takes a non-empty list of SVG commands that may contain multiple contours. +// Returns a list of lists of path segment objects that each form one contour. +// Each path segment has three keys: start, end, and control. +function split_path(path) { + assert(path.length >= 2); + assert(path[0].type === 'M', 'Path did not start with M!'); + assert(path[path.length - 1].type === 'Z', 'Path did not end with Z!'); + + var result = [[]]; + var start = [path[0].x, path[0].y]; + var current = clone(start); + + for (var i = 1; i < path.length; i++) { + var command = path[i]; + if (command.type === 'M' || command.type === 'Z') { + assert(start.x === current.x && start.y === current.y, 'Open contour!'); + assert(result[result.length -1].length > 0, 'Empty contour!'); + if (command.type === 'Z') { + assert(i === path.length - 1, 'Path ended early!'); + return result; + } + var start = {x: command.x, y: command.y}; + var current = {x: start_point.x, y: start_point.y}; + continue; + } + assert(command.type === 'Q', 'Got unexpected TTF command: ' + command.type); + var segment = { + 'start': clone(current), + 'end': [command.x, command.y], + 'control': [command.x1, command.y1], + }; + result[result.length - 1].push(segment); + current = clone(segment.end); + } +} + +// Takes a list of paths. Returns them oriented the way a TTF glyph should be: +// exterior contours counter-clockwise and interior contours clockwise. +function orient_paths(paths) { + var max_area = 0; + for (var i = 0; i < paths.length; i++) { + var area = get_2x_area(paths[i]); + if (Math.abs(area) > max_area) { + max_area = area; + } + } + if (max_area < 0) { + // The paths are reversed. Flip each one. + var result = []; + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + for (var j = 0; j < paths.length; j++) { + var ref = [path[j].start, path[j].end]; + path[j].start = ref[1]; + path[j].end = ref[0]; + } + path[j].reverse(); + } + } + return paths; +} + +// Returns twice the area contained in the path. The result is positive iff the +// path winds in the counter-clockwise direction. +function get_2x_area(path) { + var area = 0; + for (var i = 0; i < path.length; i++) { + var segment = path[i]; + area += (segment.end.x - segment.start.x)*(segment.end.y + segment.start.y); + } + return area; +} + +split_and_orient_path = function(path) { + return orient_paths(split_path(path)); +} diff --git a/server/glyph.js b/server/glyphs.js similarity index 88% rename from server/glyph.js rename to server/glyphs.js index f45aab78..e67a6a5a 100644 --- a/server/glyph.js +++ b/server/glyphs.js @@ -1,3 +1,9 @@ +function save_glyph(glyph) { + check(glyph.name, String); + Glyphs.upsert({name: glyph.name}, glyph); + return glyph; +} + Meteor.methods({ get_glyph: function(name) { return Glyphs.findOne({name: name}); @@ -26,11 +32,10 @@ Meteor.methods({ return prev ? prev : Glyphs.findOne( {'manual.verified': {$ne: true}}, {sort: {name: -1}}); }, + save_glyph: save_glyph, save_glyphs: function(glyphs) { for (var i = 0; i < glyphs.length; i++) { - var glyph = glyphs[i]; - check(glyph.name, String); - Glyphs.upsert({name: glyph.name}, glyph); + save_glyph(glyphs[i]); } }, });