mirror of
https://github.com/skishore/makemeahanzi.git
synced 2025-10-30 02:18:16 +08:00
Port split_and_orient_path to Javascript
This commit is contained in:
@ -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;
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Glyphs = new Mongo.Collection('glyphs');
|
||||
102
lib/glyphs.js
Normal file
102
lib/glyphs.js
Normal file
@ -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));
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user