mirror of
				https://github.com/skishore/makemeahanzi.git
				synced 2025-10-31 10:56:39 +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) { | function change_glyph(method, glyph) { | ||||||
|   glyph = glyph || Session.get('glyph.data'); |   glyph = glyph || Session.get('glyph.data'); | ||||||
|   Meteor.call(method, glyph, function(error, data) { |   Meteor.call(method, glyph, function(error, data) { | ||||||
|  |     data.d = Glyphs.get_svg_path(data); | ||||||
|  |  | ||||||
|     data.manual = data.manual || {}; |     data.manual = data.manual || {}; | ||||||
|     data.manual.bridges_added = data.manual.bridges_added || []; |     data.manual.bridges_added = data.manual.bridges_added || []; | ||||||
|     data.manual.bridges_removed = data.manual.bridges_removed || []; |     data.manual.bridges_removed = data.manual.bridges_removed || []; | ||||||
|     data.manual.verified = data.manual.verified || false; |     data.manual.verified = data.manual.verified || false; | ||||||
|  |  | ||||||
|     Session.set('glyph.data', data); |     Session.set('glyph.data', data); | ||||||
|     if (method != 'save_glyph') { |     if (method != 'save_glyph') { | ||||||
|       Session.set('glyph.show_strokes', true); |       Session.set('glyph.show_strokes', true); | ||||||
| @ -181,6 +184,7 @@ Template.glyph.helpers({ | |||||||
|     return !!Session.get('glyph.show_strokes'); |     return !!Session.get('glyph.show_strokes'); | ||||||
|   }, |   }, | ||||||
|   strokes: function() { |   strokes: function() { | ||||||
|  |     return []; | ||||||
|     var glyph = Session.get('glyph.data'); |     var glyph = Session.get('glyph.data'); | ||||||
|     var result = []; |     var result = []; | ||||||
|     var strokes = glyph.extractor.strokes; |     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({ | Meteor.methods({ | ||||||
|   get_glyph: function(name) { |   get_glyph: function(name) { | ||||||
|     return Glyphs.findOne({name: name}); |     return Glyphs.findOne({name: name}); | ||||||
| @ -26,11 +32,10 @@ Meteor.methods({ | |||||||
|     return prev ? prev : Glyphs.findOne( |     return prev ? prev : Glyphs.findOne( | ||||||
|       {'manual.verified': {$ne: true}}, {sort: {name: -1}}); |       {'manual.verified': {$ne: true}}, {sort: {name: -1}}); | ||||||
|   }, |   }, | ||||||
|  |   save_glyph: save_glyph, | ||||||
|   save_glyphs: function(glyphs) { |   save_glyphs: function(glyphs) { | ||||||
|     for (var i = 0; i < glyphs.length; i++) { |     for (var i = 0; i < glyphs.length; i++) { | ||||||
|       var glyph = glyphs[i]; |       save_glyph(glyphs[i]); | ||||||
|       check(glyph.name, String); |  | ||||||
|       Glyphs.upsert({name: glyph.name}, glyph); |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
		Reference in New Issue
	
	Block a user
	 Shaunak Kishore
					Shaunak Kishore