Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Sketchometry.activity/3dparty/hwr/handwriting.js
diff options
context:
space:
mode:
Diffstat (limited to 'Sketchometry.activity/3dparty/hwr/handwriting.js')
-rw-r--r--Sketchometry.activity/3dparty/hwr/handwriting.js854
1 files changed, 854 insertions, 0 deletions
diff --git a/Sketchometry.activity/3dparty/hwr/handwriting.js b/Sketchometry.activity/3dparty/hwr/handwriting.js
new file mode 100644
index 0000000..3ec56a1
--- /dev/null
+++ b/Sketchometry.activity/3dparty/hwr/handwriting.js
@@ -0,0 +1,854 @@
+var JXGHWR = {
+
+ strokes: [],
+ points: [],
+ pointsRaw: [],
+ pointCloudsRaw: [],
+
+ strokeID: 0,
+ lastDate: 0,
+ startDate: 0,
+ minTimeDiff: 30,
+ minStrokeLength: 5,
+ maxDistance: 20,
+
+ types: {
+ 'D': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
+ 'Sep': [',', '.'],
+ 'L': ['x', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'l', 'n', 'o', 's', 't'],
+ 'Op': ['+', '-', '*', '/', '^'],
+ 'K': ['(', ')','|'],
+ 'Ot': ['sqrt', '='],
+ 'V': ['x', 'y']
+ },
+
+ recognizerP: null,
+ recognizerN: null,
+
+ /**
+ * Canvas variables
+ */
+
+ canvas: null,
+ g: null,
+ rc: null,
+ timeout: null,
+
+ /**
+ *
+ */
+
+ needsClear: false,
+ isDown: false,
+
+ init: function(cv, tb) {
+
+ this.strokes = [];
+ this.points = [];
+ this.pointsRaw = [];
+
+ this.strokeID = 0;
+
+ this.canvas = cv;
+ this.textbox = tb;
+
+ this.initCanvas();
+
+ this.lastDate = new Date();
+
+ this.recognizerP = new JXGHWR_PDollarRecognizer();
+ this.recognizerN = new JXGHWR_NDollarRecognizer(true);
+
+ this.readGesturesFromLocalStorage();
+ },
+
+ readGesturesFromLocalStorage: function() {
+ var pointCloudsRaw, i, le;
+
+ if (localStorage) // otherwise IE 10 complains
+ pointCloudsRaw = JSON.parse(localStorage.getItem('JXGPointClouds'));
+
+ if (pointCloudsRaw!=null) {
+
+ this.pointCloudsRaw = pointCloudsRaw;
+ for (i = 0; i < pointCloudsRaw.length; ++i) {
+ this.recognizerP.AddGesture(
+ pointCloudsRaw[i].Name,
+ this.convertPoints(pointCloudsRaw[i].Points, 'p')
+ );
+
+ le = pointCloudsRaw[i].Points.length;
+ if (pointCloudsRaw[i].Points[le - 1][2] === 1) { // unistroke gesture
+ this.recognizerN.AddGesture(
+ pointCloudsRaw[i].Name,
+ true,
+ [this.convertPoints(pointCloudsRaw[i].Points)]
+ );
+ }
+ }
+ }
+
+ for (i = 0; i < this.recognizerP.PointClouds.length; ++i) {
+ le = this.recognizerP.PointClouds[i].Points.length;
+ this.recognizerP.PointClouds[i].NrStrokes = this.recognizerP.PointClouds[i].Points[le - 1].ID;
+ }
+ },
+
+ addGestureRaw: function(name, points) {
+ this.pointCloudsRaw.push({
+ Name: name,
+ Points: points.slice(0)
+ });
+ },
+
+ convertPoints: function(pointsRaw, type) {
+ var i, le, points, p;
+
+ points = [];
+ le = pointsRaw.length;
+ for (i = 0; i < le; ++i) {
+ p = {
+ X: pointsRaw[i][0],
+ Y: pointsRaw[i][1]
+ };
+
+ if (type === 'p') {
+ p.ID = pointsRaw[i][2] ;
+ }
+ points.push(p);
+ }
+ return points;
+ },
+
+ convertPointsN: function(pcRaw) {
+ var i, pc, le, name, points, p;
+
+ name = pcRaw.Name;
+ points = [];
+ le = pcRaw.Points.length;
+ for (i = 0; i < le; ++i) {
+ p = {
+ X: pcRaw.Points[i][0],
+ Y: pcRaw.Points[i][1]
+ };
+ points.push(p);
+ }
+ pc = new this.recognizerN.AddMultistroke(name, true, [points]);
+
+ return pc;
+ },
+
+ getBBox: function(stroke) {
+ var i, le = stroke.length, bb = [Infinity, Infinity, 0, 0];
+
+ for(i = 0; i < le; i++) {
+ bb[0] = (stroke[i].X < bb[0]) ? stroke[i].X : bb[0];
+ bb[1] = (stroke[i].Y < bb[1]) ? stroke[i].Y : bb[1];
+ bb[2] = (stroke[i].X > bb[2]) ? stroke[i].X : bb[2];
+ bb[3] = (stroke[i].Y > bb[3]) ? stroke[i].Y : bb[3];
+ }
+ return bb;
+ },
+
+ getType: function(c) {
+ var item, i, le;
+
+ for (item in this.types) {
+ le = this.types[item].length;
+ for (i = 0; i < le; i++) {
+ if (c === this.types[item][i]) {
+ return item;
+ }
+ }
+ }
+ return '';
+ },
+
+ getStrokeNr: function(stroke) {
+ return stroke[stroke.length - 1].ID;
+ },
+
+ makeGoodlist: function(c, txt, lastbb, actbb) {
+ var list = [], t, last2 = '',
+ height = lastbb[3] - lastbb[1];
+
+ if (height > 1) {
+ height *= 0.5;
+ }
+
+ if (txt.length > 1) {
+ last2 = txt.slice(-2);
+ }
+
+ t = this.getType(c);
+ if (c === '') {
+ list = list.concat(['+', '-']);
+ list = list.concat(this.types['D']);
+ list = list.concat(['(']);
+ list = list.concat(this.types['L']);
+ list = list.concat(this.types['V']);
+ list = list.concat(this.types['Ot']);
+ } else if (c !== '(' && c !== '|' && c !== '/' &&
+ actbb[3] - actbb[1] < 40 &&
+ lastbb[1] + 30 < actbb[1] && // if char is roughly in the middle of the previous it is - or +.
+ lastbb[3] - 30 > actbb[3]) {
+ list = list.concat(['+', '-']);
+ } else if (t === 'V') {
+ list = list.concat(this.types['Op']);
+ list = list.concat(this.types['K']);
+ } else if (t === 'D') {
+ list = list.concat(this.types['D']);
+ list = list.concat(this.types['V']);
+ list = list.concat(['sqrt']);
+
+ if (lastbb[3] + 5 < actbb[3] && // char has to be reach slichtly below last char
+ lastbb[3] - height < actbb[1]) { // char may reach at most to the middle of last char
+ list = list.concat([',']);
+ // Don't allow (e)l.
+ list = list.concat(['x', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'n', 'o', 's', 't']);
+ } else {
+ list = list.concat(this.types['Op']);
+ list = list.concat(this.types['K']);
+ list = list.concat(this.types['L']);
+ }
+
+ } else if (t === 'Op') {
+ list = list.concat(this.types['D']);
+ list = list.concat(['(']);
+ //list = list.concat(this.types['L']);
+ list = list.concat(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'l', 's', 't']);
+ list = list.concat(this.types['V']);
+ list = list.concat(['sqrt']);
+ } else if (c === '(') {
+ list = list.concat(this.types['D']);
+ list = list.concat(['(']);
+ list = list.concat(this.types['L']);
+ list = list.concat(this.types['V']);
+ list = list.concat(['sqrt']);
+ } else if (c === ')') {
+ list = list.concat(this.types['Op']);
+ list = list.concat(')');
+
+ //list = list.concat(this.types['D']);
+ //list = list.concat(this.types['L']);
+ //list = list.concat(this.types['V']);
+
+ } else if (last2 === 'si' || last2 === 'ta') {
+ list = list.concat(['n']);
+ } else if (last2 === 'co') {
+ list = list.concat(['s']);
+ } else if (last2 === 'lo') {
+ list = list.concat(['g']);
+ } else if (t === 'L') {
+ list = list.concat(this.types['V']);
+ list = list.concat(this.types['Op']);
+ list = list.concat(this.types['K']);
+ if (c === 's') {
+ list = list.concat(['i']); // sin
+ } else if (c === 'c') {
+ list = list.concat(['o']); // cos
+ } else if (c === 'l') {
+ list = list.concat(['o', 'n']); // log, ln
+ } else if (c === 't') {
+ list = list.concat(['a']); // ta
+ }
+ } else {
+ list = list.concat(this.types['D']);
+ list = list.concat(this.types['Op']);
+ list = list.concat(this.types['K']);
+ list = list.concat(this.types['L']);
+ list = list.concat(this.types['V']);
+ list = list.concat(this.types['Ot']);
+ }
+
+ return list;
+ },
+
+ /**
+ * Sort strokes from left to right
+ */
+ sortStrokes: function() {
+ var that = this;
+ this.strokes.sort(function(a,b){
+ var bba = that.getBBox(a), bbb = that.getBBox(b);
+ return bba[0]-bbb[0];
+ });
+ },
+
+ /**
+ * Collect left-over stroke before starting recognizerP.
+ */
+ collectStroke: function() {
+ if (this.strokeID !== 0) {
+ this.startDate = new Date();
+ if (this.startDate.getTime() - this.lastDate.getTime() > this.minTimeDiff) {
+ // Copy points for dollar p algorithm
+ this.strokes.push( this.points.slice(0) );
+ this.strokeID = 0;
+ }
+ }
+ },
+
+ /**
+ * Combine strokes which intersect each other or which are connected
+ * into single strokes.
+ */
+ recombineStrokes: function() {
+ var i, j, sid, le;
+
+
+ if (this.strokes.length > 1) {
+ for (i = 1; i < this.strokes.length; i++) {
+ if (this.isConnected(this.strokes[i-1], this.strokes[i]) ||
+ this.isCrossing(this.strokes[i-1], this.strokes[i])) {
+
+ // Adapt strokeID
+ le = this.strokes[i-1].length;
+ sid = this.strokes[i-1][le-1].ID;
+
+ le = this.strokes[i].length;
+ for (j = 0; j < le; j++) {
+ this.strokes[i][j].ID += sid;
+ }
+
+ // Copy stroke from stroke[i] to stroke[i-1]
+ this.strokes[i-1] = this.strokes[i-1].concat( this.strokes[i].slice(0) );
+ this.strokes.splice(i, 1);
+ }
+ }
+ }
+ },
+
+ /**
+ * Input: last char and two bounding boxes.
+ */
+ isExponent: function(last, lastbb, actbb) {
+ var height = lastbb[3] - lastbb[1];
+
+ if (height > 1) {
+ height *= 0.5;
+ }
+
+ if (last !== '.' && last !== ',' && last !== '-' &&
+ // top of char is slightly above top of last char
+ lastbb[1] > actbb[1] + 5 &&
+ // bottom of char is above middle height of last char
+ lastbb[3] - height > actbb[3]
+ ) {
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Input: last bounding box and actual bounding box.
+ */
+ isEndExponent: function(lastbb, actbb) {
+ var height = lastbb[3] - lastbb[1];
+
+ if (height > 1) {
+ height *= 2.0/3.0;
+ }
+
+ if (
+ // bottom of char is below two third of height of last char
+ lastbb[3] - height < actbb[1] &&
+ lastbb[3] < actbb[3]
+ ) {
+
+ return true;
+ }
+
+ return false;
+ },
+
+ recognize: function() {
+ var txt = '',
+ result,
+ quality = '',
+ last = '',
+ i, list,
+ lastbb = [0,0,0,0], bb,
+ expLevel = 0,
+ botLine = [],
+ sqrtLevel = 0,
+ sqrtLine = [],
+ sqrtEnd = [],
+ st;
+
+ //st = new Date();
+
+ this.collectStroke();
+ this.sortStrokes();
+ this.recombineStrokes();
+
+ /**
+ * Apply $P or $N to every stroke.
+ */
+ for (i = 0; i < this.strokes.length; i++) {
+
+ // this.paintBBox(this.strokes[i]);
+ bb = this.getBBox(this.strokes[i]);
+
+
+ // Detect end of square roots
+ while (sqrtLevel > 0 && bb[2] > sqrtEnd[sqrtLevel]) {
+ last = ')';
+ txt += last;
+ lastbb[3] = sqrtLine[expLevel];
+ --sqrtLevel;
+ }
+ // Detect end of exponent
+ while (expLevel > 0 && this.isEndExponent(lastbb, bb)) {
+ last = ')';
+ txt += last;
+ --expLevel;
+ lastbb[3] = botLine[expLevel];
+ }
+
+ if (this.strokes[i].length >= this.minStrokeLength) {
+ // Detect exponents.
+ // This is needed before calling makeGoodlist(), because
+ // it influences the possible choices for the next symbol.
+ if (i>0 && this.isExponent(last, lastbb, bb)) {
+ last = '^';
+ txt += last;
+ last = '(';
+ txt += last;
+ botLine[expLevel] = lastbb[3];
+ ++expLevel;
+ }
+
+ list = this.makeGoodlist(last, txt, lastbb, bb);
+ this.strokes[i].NrStrokes = this.getStrokeNr(this.strokes[i]); // Determine number of strokes
+
+ if (this.strokes[i].NrStrokes === 1) {
+ result = this.recognizerN.Recognize([this.strokes[i]], true, false, true, list);
+ } else {
+ result = this.recognizerP.Recognize(this.strokes[i], list);
+ }
+
+ if (result.Name === '|' && bb[2] - bb[0] > 30) {
+ result.Name = '/';
+ }
+ quality += result.Score.toFixed(2) + ' ';
+
+ } else {
+ // We have a dot.
+ // There are three possibilities: ., i, or *
+ bb = this.getBBox(this.strokes[i]);
+ result = { Name:'' };
+
+ // If last symbol is digit and the dot is close to the baseline, it is a separator
+ if (last.match(/\d/) && bb[1] > lastbb[3] - 30 && bb[3] < lastbb[3] + 30) { // .
+ result.Name = '.';
+
+ // If the dot is close to the topline the last symbol is an "i"
+ } else if (bb[2] < lastbb[2] + 20 &&
+ bb[3] < lastbb[1] && bb[3] < lastbb[3]) { // i (only backward recognition)
+ result.Name = 'i';
+ txt = txt.slice(0, -1);
+ } else { // *
+ result.Name = '*';
+ }
+ // lastbb = [0,0,0,0];
+ quality += '1.0 ';
+ }
+
+ // Handle square root stretches
+ if (result.Name === 'sqrt') {
+ result.Name += '(';
+ ++sqrtLevel;
+ sqrtLine[sqrtLevel] = bb[3];
+ sqrtEnd[sqrtLevel] = bb[2];
+ }
+
+ last = result.Name;
+ lastbb = bb.slice(0);
+ txt += last;
+
+ /**
+ * Post processing. Better do it after each step.
+ */
+ // Handle '='
+ txt = txt.replace(/--/g, '=');
+ txt = txt.replace(/-\^-/g, '=');
+
+ // Handle i
+ txt = txt.replace(/,\*/g, 'i');
+ txt = txt.replace(/\|\*/g, 'i');
+ txt = txt.replace(/\(\*/g, 'i');
+ txt = txt.replace(/i\*/g, 'i');
+
+ // Handle the case 3.a which is probably 3*a
+ txt = txt.replace(/(\d)\.([a-z])/g, '$1*$2');
+
+ // Handle log, sin, cos
+ txt = txt.replace(/l0g/g, 'log');
+ txt = txt.replace(/\wi\D{1}/g, 'sin');
+ txt = txt.replace(/sin\*/g, 'sin');
+ txt = txt.replace(/co./g, 'cos');
+ txt = txt.replace(/c0s/g, 'cos');
+ }
+
+ // Add closing parenthesis for exponents and square roots
+ while (expLevel > 0) {
+ txt += ')';
+ --expLevel;
+ }
+ while (sqrtLevel > 0) {
+ txt += ')';
+ --sqrtLevel;
+ }
+
+ /**
+ * Finalize recognition
+ */
+ this.strokeID = 0; // signal to begin new gesture on next mouse-down
+
+ //txt += '\ntime:' + ((new Date()).getTime() - st.getTime());
+
+ return {
+ str: txt,
+ quality: quality
+ };
+ },
+
+ /**
+ * Euclidean distance between two "D points.
+ */
+ dist: function(a, b) {
+ return Math.sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y));
+ },
+
+ rand: function(low, high) {
+ return Math.floor((high - low + 1) * Math.random()) + low;
+ },
+
+ /**
+ * Detect if two strokes are connected
+ */
+ isConnected: function(s1, s2) {
+ var thresh = this.maxDistance, isC = false;
+
+ if (this.dist(s1[0], s2[0]) <= thresh) {
+ isC = true;
+ } else if (this.dist(s1[0], s2[s2.length - 1]) <= thresh) {
+ isC = true;
+ } else if (this.dist(s1[s1.length - 1], s2[0]) <= thresh) {
+ isC = true;
+ } else if (this.dist(s1[s1.length - 1], s2[s2.length - 1]) <= thresh) {
+ isC = true;
+ }
+
+ return isC;
+ },
+
+ /**
+ * Detect if two strokes cross each other,
+ */
+ isCrossing: function(s1, s2) {
+ var i, j, le1 = s1.length, le2 = s2.length, isC = false,
+ a1, a2, b1, b2,
+ bb1, bb2, c1, c2;
+
+ if (le1 < 2 || le2 < 2) {
+ return isC;
+ }
+
+ for (i = 1; i < le1; i++) {
+ a1 = s1[i - 1];
+ a2 = s1[i];
+ bb1 = [Math.min(a1.X, a2.X), Math.min(a1.Y, a2.Y),
+ Math.max(a1.X, a2.X), Math.max(a1.Y, a2.Y)];
+
+ for (j = 1; j < le2; j++) {
+ b1 = s2[j - 1];
+ b2 = s2[j];
+ bb2 = [Math.min(b1.X, b2.X), Math.min(b1.Y, b2.Y),
+ Math.max(b1.X, b2.X), Math.max(b1.Y, b2.Y)];
+
+ if (bb1[2] >= bb2[0] && bb1[0] <= bb2[2] && bb1[1] <= bb2[3] && bb1[3] >= bb2[1]) {
+ c1 = (b1.X - a1.X) * (a2.Y - a1.Y) - (b1.Y - a1.Y) * (a2.X - a1.X);
+ c2 = (b2.X - a1.X) * (a2.Y - a1.Y) - (b2.Y - a1.Y) * (a2.X - a1.X);
+ if (c1 * c2 <= 0.0) {
+ isC = true;
+ return isC;
+ }
+ }
+ }
+ }
+
+ return isC;
+ },
+
+ initCanvas: function() {
+
+ this.g = this.canvas.getContext('2d');
+ this.g.lineWidth = 3;
+ this.g.font = "14px Arial";
+/*
+ // Draw top bar
+ var col = "rgb(" + this.rand(0,200) + "," + this.rand(0,200) + "," + this.rand(0,200) + ")";
+
+ this.g.fillStyle = col;
+ this.g.fillRect(0, 0, this.canvas.width, 20);
+*/
+ },
+
+ /**
+ * Get dimensions and upper left corner of the canvas element.
+
+ getCanvasRect: function() {
+ var canvas = this.canvas,
+ w = canvas.width,
+ h = canvas.height,
+ cx = canvas.offsetLeft,
+ cy = canvas.offsetTop;
+
+ while (canvas.offsetParent != null) {
+ canvas = canvas.offsetParent;
+ cx += canvas.offsetLeft;
+ cy += canvas.offsetTop;
+ }
+ return {x: cx, y: cy, width: w, height: h};
+ },
+ */
+
+ getScrollY: function() {
+ //var scrollY = 0, os;
+
+ // os = $(this.canvas).position();
+ // scrollY = os.top;
+
+
+ // if (typeof(document.body.parentElement) != 'undefined') {
+ // scrollY = document.body.parentElement.scrollTop; // IE
+ // } else if (typeof(window.pageYOffset) != 'undefined') {
+ // scrollY = window.pageYOffset; // FF
+ // }
+
+ //return scrollY;
+ return 0;
+ },
+
+ clearStrokes: function() {
+ this.points.length = 0;
+ this.pointsRaw.length = 0;
+ this.strokeID = 0;
+ this.strokes.length = 0;
+
+ this.g.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ //this.drawText("Canvas cleared.");
+ this.needsClear = false;
+
+ if (this.textbox !== null) {
+ this.textbox.value = "";
+ }
+ },
+
+ downEvent: function(evt) {
+ var x = evt.pageX,
+ y = evt.pageY,
+ button = evt.button,
+ t = evt.targetTouches,
+ col;
+
+ if (t) {
+ button = 0;
+ x = t[0].pageX;
+ y = t[0].pageY;
+ }
+
+ if (this.needsClear) {
+ this.clearStrokes();
+ }
+
+ if (button <= 1 || typeof button === 'undefined') {
+ this.isDown = true;
+
+ x -= this.offsetX;
+ y -= this.offsetY;
+
+ //x -= this.rc.x;
+ //y -= this.rc.y; // - this.getScrollY();
+
+ if (this.strokeID !== 0) {
+ this.startDate = new Date();
+ if (this.startDate.getTime() - this.lastDate.getTime() > this.minTimeDiff) {
+
+ // Copy points for dollar p recognition
+ this.strokes.push( this.points.slice(0) );
+ this.strokeID = 0;
+ } else {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ if (this.strokeID === 0) { // starting a new gesture
+ this.points.length = 0;
+ this.pointsRaw.length = 0;
+
+ // set random color
+ //col = "rgb(" + this.rand(0,200) + "," + this.rand(0,200) + "," + this.rand(0,200) + ")";
+ col = "rgb(20, 30, 200)";
+
+ this.g.fillStyle = col;
+ this.g.strokeStyle = col;
+ }
+
+ // Rectangle to mark the beginning of a stroke
+ //this.g.fillRect(x - 4, y - 3, 9, 9);
+ this.g.beginPath();
+ this.g.arc(x, y, 2, 0, 2 * Math.PI, false);
+ this.g.closePath();
+ this.g.fill();
+
+ // Start new stroke
+ ++this.strokeID;
+ this.points[this.points.length] = {
+ X: x,
+ Y: y,
+ ID: this.strokeID
+ };
+ this.pointsRaw.push([x, y, this.strokeID]);
+ }
+
+ if (evt && evt.preventDefault) {
+ evt.preventDefault();
+ } else if (window.event) {
+ window.event.returnValue = false;
+ }
+ },
+
+ moveEvent: function(evt) {
+ var x = evt.pageX,
+ y = evt.pageY,
+ t = evt.targetTouches;
+
+ if (t) {
+ x = t[0].pageX;
+ y = t[0].pageY;
+ }
+
+ if (this.isDown) {
+
+ x -= this.offsetX;
+ y -= this.offsetY;
+
+ //x -= this.rc.x;
+ //y -= this.rc.y; // - this.getScrollY();
+
+ // Append point
+ this.points[this.points.length] = {
+ X: x,
+ Y: y,
+ ID: this.strokeID
+ };
+ this.pointsRaw.push([x, y, this.strokeID]);
+
+ this.drawConnectedPoint(this.pointsRaw.length - 2, this.pointsRaw.length - 1);
+ }
+ },
+
+ upEvent: function() {
+ if (this.isDown) {
+ this.isDown = false;
+ this.lastDate = new Date();
+ }
+ },
+
+ drawConnectedPoint: function(from, to) {
+ this.g.beginPath();
+ this.g.moveTo(this.pointsRaw[from][0], this.pointsRaw[from][1]);
+ this.g.lineTo(this.pointsRaw[to][0], this.pointsRaw[to][1]);
+ this.g.closePath();
+ this.g.stroke();
+ },
+
+ drawText: function(str) {
+ this.g.fillStyle = "rgb(255,255,136)";
+ this.g.fillRect(0, 0, this.canvas.width, 20);
+ this.g.fillStyle = "rgb(0,0,255)";
+ this.g.fillText(str, 1, 14);
+ },
+
+ paintBBox: function(stroke) {
+ var bb = this.getBBox(stroke),
+ col = "rgb(100,100,100)";
+
+ bb[0] -= 3;
+ bb[1] -= 3;
+ bb[2] += 3;
+ bb[3] += 3;
+
+ this.g.lineWidth = 1;
+ this.g.strokeStyle = col;
+ this.g.beginPath();
+ this.g.moveTo(bb[0], bb[1]);
+ this.g.lineTo(bb[2], bb[1]);
+ this.g.lineTo(bb[2], bb[3]);
+ this.g.lineTo(bb[0], bb[3]);
+ this.g.closePath();
+ this.g.stroke();
+ this.g.lineWidth = 3;
+ },
+
+ /**
+ * Manage gestures
+ */
+
+ onAddGesture: function(name) {
+ if (this.pointsRaw.length >= this.minStrokeLength && name.length > 0) {
+ this.addGestureRaw(name, this.pointsRaw);
+ }
+ this.clearStrokes();
+ },
+
+ onSave: function() {
+ localStorage.removeItem('JXGPointClouds');
+ localStorage.setItem('JXGPointClouds', JSON.stringify(this.pointCloudsRaw));
+ if (this.textbox !== null) {
+ this.textbox.value = this.pointCloudsRaw.length + " gestures saved";
+ }
+ },
+
+ onDeleteAll: function() {
+ localStorage.removeItem('JXGPointClouds');
+ this.pointCloudsRaw = [];
+ if (this.textbox !== null) {
+ this.textbox.value = JSON.stringify(this.pointCloudsRaw);
+ }
+ },
+
+ /**
+ * 'pointcloud' has to be a (hidden) input element of the form.
+ */
+
+ onSubmit: function() {
+ GUI.getId('pointcloud').value = JSON.stringify(this.pointCloudsRaw);
+ GUI.getId('SEND').submit();
+ },
+
+ printStats: function() {
+ var stat = {}, i, t = '';
+
+ for (i = 0; i < this.pointCloudsRaw.length; ++i) {
+ if (stat[this.pointCloudsRaw[i].Name]) {
+ stat[this.pointCloudsRaw[i].Name]++;
+ } else {
+ stat[this.pointCloudsRaw[i].Name] = 1;
+ }
+ }
+
+ for (i in stat) {
+ t += '"' + i + '": ' + stat[i] + '\n';
+ }
+
+ if (this.textbox !== null) {
+ this.textbox.value = t;
+ }
+ }
+};