mirror of
https://github.com/skishore/makemeahanzi.git
synced 2025-11-02 13:01:40 +08:00
Start work on stages API
This commit is contained in:
222
client/editor.js
222
client/editor.js
@ -1,206 +1,40 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Session.setDefault('editor.glyph', undefined);
|
Session.set('editor.glyph', undefined);
|
||||||
Session.setDefault('glyph.selected_point', undefined);
|
|
||||||
|
|
||||||
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
let stage = new stages.AbstractStage;
|
||||||
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
|
||||||
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
|
|
||||||
|
|
||||||
function change_glyph(method, argument) {
|
const changeGlyph = (method, argument) => {
|
||||||
argument = argument || Session.get('editor.glyph');
|
argument = argument || Session.get('editor.glyph');
|
||||||
Meteor.call(method, argument, function(err, data) {
|
Meteor.call(method, argument, function(err, data) {
|
||||||
Session.set('editor.glyph', data);
|
Session.set('editor.glyph', data);
|
||||||
|
stage = new stages.strokes(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.get_glyph = function(name) {
|
this.getGlyph = (selector) => changeGlyph('getGlyph', selector);
|
||||||
change_glyph('get_glyph', name);
|
|
||||||
}
|
|
||||||
|
|
||||||
function contains(bridges, bridge) {
|
const initialize = () => {
|
||||||
return remove_bridges(bridges, [bridge]).length < bridges.length;
|
const glyph = Session.get('editor.glyph');
|
||||||
}
|
if (glyph === undefined) {
|
||||||
|
changeGlyph('getNextGlyph');
|
||||||
function remove_bridges(add, remove) {
|
} else {
|
||||||
var set = {};
|
getGlyph({character: glyph.character});
|
||||||
for (var i = 0; i < remove.length; i++) {
|
|
||||||
set[to_line(remove[i]).coordinates] = true;
|
|
||||||
set[to_line([remove[i][1], remove[i][0]]).coordinates] = true;
|
|
||||||
}
|
}
|
||||||
return add.filter(function(x) { return !set[to_line(x).coordinates]; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function to_line(pairs) {
|
Template.editor.events({
|
||||||
return {
|
'click svg .selectable': function(event) {
|
||||||
x1: pairs[0][0],
|
// We avoid the arrow function here so that this is bound to the template.
|
||||||
y1: pairs[0][1],
|
stage.handleEvent(event, this);
|
||||||
x2: pairs[1][0],
|
}
|
||||||
y2: pairs[1][1],
|
|
||||||
coordinates: pairs[0].join(',') + ',' + pairs[1].join(','),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function to_point(pair) {
|
|
||||||
return {x: pair[0], y: pair[1], coordinates: pair.join(',')};
|
|
||||||
}
|
|
||||||
|
|
||||||
var bindings = {
|
|
||||||
'w': function() {
|
|
||||||
if (!EDIT_STROKES) {
|
|
||||||
var glyph = Session.get('editor.glyph');
|
|
||||||
var character = String.fromCodePoint(parseInt(glyph.name.substr(3), 16));
|
|
||||||
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Session.get('glyph.show_strokes')) {
|
|
||||||
Session.set('glyph.show_strokes', false);
|
|
||||||
Session.set('glyph.selected_point', undefined);
|
|
||||||
var glyph = Session.get('editor.glyph');
|
|
||||||
glyph.manual.verified = false;
|
|
||||||
change_glyph('save_glyph', glyph);
|
|
||||||
} else {
|
|
||||||
var glyph = Session.get('editor.glyph');
|
|
||||||
delete glyph.manual;
|
|
||||||
Session.set('editor.glyph', fill_glyph_fields(glyph));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'a': function() {
|
|
||||||
change_glyph('get_previous_glyph');
|
|
||||||
},
|
|
||||||
'A': function() {
|
|
||||||
change_glyph('get_previous_glyph_skip_verified');
|
|
||||||
},
|
|
||||||
's': function() {
|
|
||||||
if (!EDIT_STROKES) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var glyph = Session.get('editor.glyph');
|
|
||||||
if (!Session.get('glyph.show_strokes')) {
|
|
||||||
Session.set('glyph.show_strokes', true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (glyph.manual.verified || !has_errors(glyph)) {
|
|
||||||
glyph.manual.verified = !glyph.manual.verified;
|
|
||||||
change_glyph('save_glyph', glyph);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'd': function() {
|
|
||||||
change_glyph('get_next_glyph');
|
|
||||||
},
|
|
||||||
'D': function() {
|
|
||||||
change_glyph('get_next_glyph_skip_verified');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Template.controls.helpers({
|
|
||||||
w_button_name: function() {
|
|
||||||
if (!EDIT_STROKES) {
|
|
||||||
return 'Check';
|
|
||||||
}
|
|
||||||
return Session.get('glyph.show_strokes') ? 'Edit' : 'Reset';
|
|
||||||
},
|
|
||||||
s_button_name: function() {
|
|
||||||
return '(Error)';
|
|
||||||
if (!Session.get('glyph.show_strokes')) {
|
|
||||||
return 'View';
|
|
||||||
}
|
|
||||||
var glyph = Session.get('glyph.data');
|
|
||||||
return glyph && glyph.manual.verified ? 'Flag' : 'Verify';
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.glyph.events({
|
|
||||||
'click #glyph svg g line': function(e) {
|
|
||||||
var coordinates = $(e.target).attr('data-coordinates');
|
|
||||||
var xs = coordinates.split(',').map(function(x) {
|
|
||||||
return parseInt(x, 10);
|
|
||||||
});
|
|
||||||
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
|
|
||||||
var glyph = Session.get('glyph.data');
|
|
||||||
glyph.manual.bridges = remove_bridges(glyph.manual.bridges, [bridge]);
|
|
||||||
Session.set('glyph.data', fill_glyph_fields(glyph));
|
|
||||||
Session.set('glyph.selected_point', undefined);
|
|
||||||
},
|
|
||||||
'click #glyph svg g circle': function(e) {
|
|
||||||
var coordinates = $(e.target).attr('data-coordinates');
|
|
||||||
var selected_point = Session.get('glyph.selected_point');
|
|
||||||
if (selected_point === coordinates) {
|
|
||||||
Session.set('glyph.selected_point', undefined);
|
|
||||||
return;
|
|
||||||
} else if (!selected_point) {
|
|
||||||
Session.set('glyph.selected_point', coordinates);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var xs = (coordinates + ',' + selected_point).split(',').map(function(x) {
|
|
||||||
return parseInt(x, 10);
|
|
||||||
});
|
|
||||||
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
|
|
||||||
var glyph = Session.get('glyph.data');
|
|
||||||
if (!contains(glyph.manual.bridges, bridge)) {
|
|
||||||
glyph.manual.bridges.push(bridge);
|
|
||||||
Session.set('glyph.data', fill_glyph_fields(glyph));
|
|
||||||
}
|
|
||||||
Session.set('glyph.selected_point', undefined);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
Template.editor.helpers({
|
Template.editor.helpers({
|
||||||
class() {
|
stage: () => Session.get('stage.type'),
|
||||||
return undefined;
|
paths: () => Session.get('stage.paths'),
|
||||||
},
|
lines: () => Session.get('stage.lines'),
|
||||||
paths() {
|
points: () => Session.get('stage.points'),
|
||||||
const glyph = Session.get('editor.glyph');
|
|
||||||
if (!glyph) return;
|
|
||||||
const result = [];
|
|
||||||
for (let i = 0; i < glyph.stages.strokes.length; i++) {
|
|
||||||
result.push({
|
|
||||||
cls: 'selectable',
|
|
||||||
d: glyph.stages.strokes[i],
|
|
||||||
fill: COLORS[i % COLORS.length],
|
|
||||||
stroke: 'black',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
lines() {
|
|
||||||
return;
|
|
||||||
const glyph = Session.get('editor.glyph');
|
|
||||||
if (!glyph) return;
|
|
||||||
var original = {};
|
|
||||||
for (var i = 0; i < glyph.render.bridges.length; i++) {
|
|
||||||
var bridge = glyph.render.bridges[i];
|
|
||||||
original[to_line(bridge).coordinates] = true;
|
|
||||||
original[to_line([bridge[1], bridge[0]]).coordinates] = true;
|
|
||||||
}
|
|
||||||
var result = [];
|
|
||||||
for (var i = 0; i < glyph.manual.bridges.length; i++) {
|
|
||||||
var line = to_line(glyph.manual.bridges[i]);
|
|
||||||
line.color = original[line.coordinates] ? 'red' : 'purple';
|
|
||||||
result.push(line);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
points() {
|
|
||||||
return;
|
|
||||||
const glyph = Session.get('editor.glyph');
|
|
||||||
if (!glyph) return;
|
|
||||||
var result = [];
|
|
||||||
for (var i = 0; i < glyph.render.endpoints.length; i++) {
|
|
||||||
var endpoint = glyph.render.endpoints[i];
|
|
||||||
var point = to_point(endpoint.point);
|
|
||||||
point.color = endpoint.corner ? 'red' : 'black';
|
|
||||||
point.z_index = endpoint.corner ? 1 : 0;
|
|
||||||
if (point.coordinates === Session.get('glyph.selected_point')) {
|
|
||||||
point.color = 'purple';
|
|
||||||
}
|
|
||||||
result.push(point);
|
|
||||||
}
|
|
||||||
result.sort(function(p1, p2) { return p1.z_index - p2.z_index; });
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.metadata.helpers({
|
Template.metadata.helpers({
|
||||||
@ -222,16 +56,9 @@ Template.metadata.helpers({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Template.status.helpers({
|
Template.status.helpers({
|
||||||
stage() {
|
stage: () => Session.get('stage.type'),
|
||||||
return 'strokes';
|
instructions: () => Session.get('stage.instructions'),
|
||||||
},
|
lines: () => Session.get('stage.status'),
|
||||||
lines() {
|
|
||||||
return [
|
|
||||||
{cls: 'success', message: 'asdf'},
|
|
||||||
{cls: 'error', message: 'asdf asdf'},
|
|
||||||
{message: 'asdf asdf asdf asdf asdf asdf asdf asdf asdf asd fasd fasd fasd fas dfa sdfa sdf'},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.startup(function() {
|
Meteor.startup(function() {
|
||||||
@ -241,6 +68,5 @@ Meteor.startup(function() {
|
|||||||
bindings[key]();
|
bindings[key]();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cjklib.promise.then(() => change_glyph('get_next_glyph'))
|
cjklib.promise.then(initialize).catch(console.error.bind(console));
|
||||||
.catch(console.error.bind(console));
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -56,13 +56,11 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
{{#each lines}}
|
{{#each lines}}
|
||||||
<line class="{{cls}}" stroke="{{stroke}}" stroke-width="8"
|
<line class="{{cls}}" stroke="{{stroke}}" stroke-width="8"
|
||||||
x1={{x1}} y1={{y1}} x2={{x2}} y2={{y2}}
|
x1={{x1}} y1={{y1}} x2={{x2}} y2={{y2}}></line>
|
||||||
data-coordinates="{{coordinates}}"></line>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#each points}}
|
{{#each points}}
|
||||||
<circle class="{{cls}}" fill="{{fill}}" stroke="{{stroke}}"
|
<circle class="{{cls}}" fill="{{fill}}" stroke="{{stroke}}"
|
||||||
cx={{cx}} cy={{cy}} r="8"
|
cx={{cx}} cy={{cy}} r="8"></circle>
|
||||||
data-coordinates="{{coordinates}}"></circle>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
@ -88,6 +86,7 @@
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">Edit {{stage}}</h3>
|
<h3 class="panel-title">Edit {{stage}}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="instructions">{{instructions}}</div>
|
||||||
<ul class="log">
|
<ul class="log">
|
||||||
{{#each lines}}
|
{{#each lines}}
|
||||||
<li class="line {{cls}}">{{message}}</li>
|
<li class="line {{cls}}">{{message}}</li>
|
||||||
|
|||||||
35
client/lib/abstract.js
Normal file
35
client/lib/abstract.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
if (this.stages !== undefined) throw new Error('Redifining stages global!');
|
||||||
|
this.stages = {};
|
||||||
|
|
||||||
|
stages.AbstractStage = class AbstractStage {
|
||||||
|
constructor(glyph) {
|
||||||
|
// Session variables the interface by which the stage interacts with UI:
|
||||||
|
// - type - String type of this stage.
|
||||||
|
// - paths - list of dicts with keys in [cls, d, fill, stroke].
|
||||||
|
// - lines - list of dicts with keys in [cls, stroke, x1, y1, x2, y2].
|
||||||
|
// - points - list of dicts with keys in [cls, cx, cy, fill, stroke].
|
||||||
|
// - instructions - String instructions for the user
|
||||||
|
// - status - list of dicts with keys in [cls, message] to log.
|
||||||
|
//
|
||||||
|
// The class name 'selectable' is special for paths, lines, and points.
|
||||||
|
// Including this class in cls for those objects will make them interactive
|
||||||
|
// and will trigger the onClick callback when they are clicked.
|
||||||
|
Session.set('stage.type', undefined);
|
||||||
|
Session.set('stage.paths', undefined);
|
||||||
|
Session.set('stage.lines', undefined);
|
||||||
|
Session.set('stage.points', undefined);
|
||||||
|
Session.set('stage.instructions', undefined);
|
||||||
|
Session.set('stage.status', undefined);
|
||||||
|
this.glyph = glyph;
|
||||||
|
}
|
||||||
|
getColors() {
|
||||||
|
return ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
||||||
|
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
||||||
|
}
|
||||||
|
handleEvent(event, template) {
|
||||||
|
assert(false, 'handleEvent was not implemented!');
|
||||||
|
}
|
||||||
|
refresh(glyph) {
|
||||||
|
assert(false, 'refresh was not implemented!');
|
||||||
|
}
|
||||||
|
}
|
||||||
245
client/lib/old.js
Normal file
245
client/lib/old.js
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
"use strict";
|
||||||
|
/*
|
||||||
|
Session.setDefault('editor.glyph', undefined);
|
||||||
|
Session.setDefault('glyph.selected_point', undefined);
|
||||||
|
|
||||||
|
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
||||||
|
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
||||||
|
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
|
||||||
|
|
||||||
|
function change_glyph(method, argument) {
|
||||||
|
argument = argument || Session.get('editor.glyph');
|
||||||
|
Meteor.call(method, argument, function(err, data) {
|
||||||
|
Session.set('editor.glyph', data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.get_glyph = function(name) {
|
||||||
|
change_glyph('get_glyph', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function contains(bridges, bridge) {
|
||||||
|
return remove_bridges(bridges, [bridge]).length < bridges.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_bridges(add, remove) {
|
||||||
|
var set = {};
|
||||||
|
for (var i = 0; i < remove.length; i++) {
|
||||||
|
set[to_line(remove[i]).coordinates] = true;
|
||||||
|
set[to_line([remove[i][1], remove[i][0]]).coordinates] = true;
|
||||||
|
}
|
||||||
|
return add.filter(function(x) { return !set[to_line(x).coordinates]; });
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_line(pairs) {
|
||||||
|
return {
|
||||||
|
x1: pairs[0][0],
|
||||||
|
y1: pairs[0][1],
|
||||||
|
x2: pairs[1][0],
|
||||||
|
y2: pairs[1][1],
|
||||||
|
coordinates: pairs[0].join(',') + ',' + pairs[1].join(','),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_point(pair) {
|
||||||
|
return {x: pair[0], y: pair[1], coordinates: pair.join(',')};
|
||||||
|
}
|
||||||
|
|
||||||
|
var bindings = {
|
||||||
|
'w': function() {
|
||||||
|
if (!EDIT_STROKES) {
|
||||||
|
var glyph = Session.get('editor.glyph');
|
||||||
|
var character = String.fromCodePoint(parseInt(glyph.name.substr(3), 16));
|
||||||
|
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Session.get('glyph.show_strokes')) {
|
||||||
|
Session.set('glyph.show_strokes', false);
|
||||||
|
Session.set('glyph.selected_point', undefined);
|
||||||
|
var glyph = Session.get('editor.glyph');
|
||||||
|
glyph.manual.verified = false;
|
||||||
|
change_glyph('save_glyph', glyph);
|
||||||
|
} else {
|
||||||
|
var glyph = Session.get('editor.glyph');
|
||||||
|
delete glyph.manual;
|
||||||
|
Session.set('editor.glyph', fill_glyph_fields(glyph));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'a': function() {
|
||||||
|
change_glyph('get_previous_glyph');
|
||||||
|
},
|
||||||
|
'A': function() {
|
||||||
|
change_glyph('get_previous_glyph_skip_verified');
|
||||||
|
},
|
||||||
|
's': function() {
|
||||||
|
if (!EDIT_STROKES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var glyph = Session.get('editor.glyph');
|
||||||
|
if (!Session.get('glyph.show_strokes')) {
|
||||||
|
Session.set('glyph.show_strokes', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (glyph.manual.verified || !has_errors(glyph)) {
|
||||||
|
glyph.manual.verified = !glyph.manual.verified;
|
||||||
|
change_glyph('save_glyph', glyph);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'd': function() {
|
||||||
|
change_glyph('get_next_glyph');
|
||||||
|
},
|
||||||
|
'D': function() {
|
||||||
|
change_glyph('get_next_glyph_skip_verified');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.controls.helpers({
|
||||||
|
w_button_name: function() {
|
||||||
|
if (!EDIT_STROKES) {
|
||||||
|
return 'Check';
|
||||||
|
}
|
||||||
|
return Session.get('glyph.show_strokes') ? 'Edit' : 'Reset';
|
||||||
|
},
|
||||||
|
s_button_name: function() {
|
||||||
|
return '(Error)';
|
||||||
|
if (!Session.get('glyph.show_strokes')) {
|
||||||
|
return 'View';
|
||||||
|
}
|
||||||
|
var glyph = Session.get('glyph.data');
|
||||||
|
return glyph && glyph.manual.verified ? 'Flag' : 'Verify';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.glyph.events({
|
||||||
|
'click #glyph svg g line': function(e) {
|
||||||
|
var coordinates = $(e.target).attr('data-coordinates');
|
||||||
|
var xs = coordinates.split(',').map(function(x) {
|
||||||
|
return parseInt(x, 10);
|
||||||
|
});
|
||||||
|
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
|
||||||
|
var glyph = Session.get('glyph.data');
|
||||||
|
glyph.manual.bridges = remove_bridges(glyph.manual.bridges, [bridge]);
|
||||||
|
Session.set('glyph.data', fill_glyph_fields(glyph));
|
||||||
|
Session.set('glyph.selected_point', undefined);
|
||||||
|
},
|
||||||
|
'click #glyph svg g circle': function(e) {
|
||||||
|
var coordinates = $(e.target).attr('data-coordinates');
|
||||||
|
var selected_point = Session.get('glyph.selected_point');
|
||||||
|
if (selected_point === coordinates) {
|
||||||
|
Session.set('glyph.selected_point', undefined);
|
||||||
|
return;
|
||||||
|
} else if (!selected_point) {
|
||||||
|
Session.set('glyph.selected_point', coordinates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var xs = (coordinates + ',' + selected_point).split(',').map(function(x) {
|
||||||
|
return parseInt(x, 10);
|
||||||
|
});
|
||||||
|
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
|
||||||
|
var glyph = Session.get('glyph.data');
|
||||||
|
if (!contains(glyph.manual.bridges, bridge)) {
|
||||||
|
glyph.manual.bridges.push(bridge);
|
||||||
|
Session.set('glyph.data', fill_glyph_fields(glyph));
|
||||||
|
}
|
||||||
|
Session.set('glyph.selected_point', undefined);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.editor.helpers({
|
||||||
|
class() {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
paths() {
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < glyph.stages.strokes.length; i++) {
|
||||||
|
result.push({
|
||||||
|
cls: 'selectable',
|
||||||
|
d: glyph.stages.strokes[i],
|
||||||
|
fill: COLORS[i % COLORS.length],
|
||||||
|
stroke: 'black',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
lines() {
|
||||||
|
return;
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
var original = {};
|
||||||
|
for (var i = 0; i < glyph.render.bridges.length; i++) {
|
||||||
|
var bridge = glyph.render.bridges[i];
|
||||||
|
original[to_line(bridge).coordinates] = true;
|
||||||
|
original[to_line([bridge[1], bridge[0]]).coordinates] = true;
|
||||||
|
}
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < glyph.manual.bridges.length; i++) {
|
||||||
|
var line = to_line(glyph.manual.bridges[i]);
|
||||||
|
line.color = original[line.coordinates] ? 'red' : 'purple';
|
||||||
|
result.push(line);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
points() {
|
||||||
|
return;
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < glyph.render.endpoints.length; i++) {
|
||||||
|
var endpoint = glyph.render.endpoints[i];
|
||||||
|
var point = to_point(endpoint.point);
|
||||||
|
point.color = endpoint.corner ? 'red' : 'black';
|
||||||
|
point.z_index = endpoint.corner ? 1 : 0;
|
||||||
|
if (point.coordinates === Session.get('glyph.selected_point')) {
|
||||||
|
point.color = 'purple';
|
||||||
|
}
|
||||||
|
result.push(point);
|
||||||
|
}
|
||||||
|
result.sort(function(p1, p2) { return p1.z_index - p2.z_index; });
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.metadata.helpers({
|
||||||
|
character() {
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
return glyph.character;
|
||||||
|
},
|
||||||
|
items() {
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
const defaults = cjklib.getCharacterData(glyph.character);
|
||||||
|
const fields = ['definition', 'pinyin', 'strokes']
|
||||||
|
return fields.map((x) => ({
|
||||||
|
field: `${x[0].toUpperCase()}${x.substr(1)}:`,
|
||||||
|
value: glyph.metadata[x] || defaults[x] || '(unknown)',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.status.helpers({
|
||||||
|
stage() {
|
||||||
|
return 'strokes';
|
||||||
|
},
|
||||||
|
lines() {
|
||||||
|
return [
|
||||||
|
{cls: 'success', message: 'asdf'},
|
||||||
|
{cls: 'error', message: 'asdf asdf'},
|
||||||
|
{message: 'asdf asdf asdf asdf asdf asdf asdf asdf asdf asd fasd fasd fasd fas dfa sdfa sdf'},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.startup(function() {
|
||||||
|
$('body').on('keypress', function(e) {
|
||||||
|
var key = String.fromCharCode(e.which);
|
||||||
|
if (bindings.hasOwnProperty(key)) {
|
||||||
|
bindings[key]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cjklib.promise.then(() => change_glyph('get_next_glyph'))
|
||||||
|
.catch(console.error.bind(console));
|
||||||
|
});
|
||||||
|
*/
|
||||||
52
client/lib/strokes.js
Normal file
52
client/lib/strokes.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const getStatusLine = (actual, expected) => {
|
||||||
|
const actual_text = `Extracted ${actual} stroke${actual === 1 ? '' : 's'}`;
|
||||||
|
if (expected === undefined || expected === null) {
|
||||||
|
return {cls: 'error', message: `${actual_text}. True number unknown.`};
|
||||||
|
} else if (actual !== expected) {
|
||||||
|
return {cls: 'error', message: `${actual_text}, but expected ${expected}.`};
|
||||||
|
}
|
||||||
|
return {cls: 'success', message: `${actual_text}.`};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStrokePaths = (strokes, include, colors) => {
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < strokes.length; i++) {
|
||||||
|
const stroke = strokes[i];
|
||||||
|
const color = include[stroke] ? colors[i % colors.length] : 'gray';
|
||||||
|
result.push({cls: 'selectable', d: stroke, fill: color, stroke: 'black'});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
stages.strokes = class StrokesStage extends stages.AbstractStage {
|
||||||
|
constructor(glyph) {
|
||||||
|
super(glyph);
|
||||||
|
Session.set('stage.type', 'strokes');
|
||||||
|
Session.set('stage.instructions',
|
||||||
|
'Choose paths to include in the glyph by clicking on them. ' +
|
||||||
|
'The final number of paths must agree with the stroke count ' +
|
||||||
|
'in the character metadata.');
|
||||||
|
const include = this.include = {};
|
||||||
|
this.strokes = glyph.stages.strokes;
|
||||||
|
this.strokes.map((stroke) => include[stroke] = true);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
handleEvent(event, template) {
|
||||||
|
assert(this.include.hasOwnProperty(template.d));
|
||||||
|
this.include[template.d] = !this.include[template.d];
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
refresh(glyph) {
|
||||||
|
this.glyph = glyph || this.glyph;
|
||||||
|
this.glyph.stages.strokes = this.strokes.filter((x) => this.include[x]);
|
||||||
|
Session.set('editor.glyph', this.glyph);
|
||||||
|
Session.set('stage.paths',
|
||||||
|
getStrokePaths(this.strokes, this.include, super.getColors()));
|
||||||
|
const data = cjklib.getCharacterData(this.glyph.character);
|
||||||
|
const actual = this.glyph.stages.strokes.length;
|
||||||
|
const expected = this.glyph.metadata.strokes || data.strokes;
|
||||||
|
Session.set('stage.status', [getStatusLine(actual, expected)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -54,6 +54,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
|
.instructions {
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.log {
|
.log {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user