diff --git a/web/app/sketcher/main2d.js b/web/app/sketcher/main2d.js index f88fab50..7beaa3bd 100644 --- a/web/app/sketcher/main2d.js +++ b/web/app/sketcher/main2d.js @@ -6,6 +6,10 @@ TCAD.App2D = function() { this.viewer = new TCAD.TWO.Viewer(document.getElementById('viewer')); this.boundaryLayer = new TCAD.TWO.Layer("default", TCAD.TWO.Styles.DEFAULT); this.viewer.layers.push(this.boundaryLayer); + this.dmp = new diff_match_patch(); + this.historyPointer = -1; + this.diffs = []; + var app = this; this.actions = {}; @@ -18,6 +22,18 @@ TCAD.App2D = function() { app._actionsOrder.push(id); }; + this.registerAction('undo', "Undo", function () { + app.undo(); + }); + + this.registerAction('redo', "Redo", function () { + app.redo(); + }); + + this.registerAction('checkpoint', "Checkpoint", function () { + app.checkpoint(); + }); + this.registerAction('addPoint', "Add Point", function () { app.viewer.toolManager.takeControl(new TCAD.TWO.AddPointTool(app.viewer)); }); @@ -58,58 +74,8 @@ TCAD.App2D = function() { }); this.registerAction('save', "Save", function () { - var sketch = {}; - //sketch.boundary = boundary; - sketch.layers = []; - function point(p) { - return [ p.id, [p._x.id, p.x], [p._y.id, p.y] ]; - } - var toSave = [app.viewer.dimLayers, app.viewer.layers] - for (var t = 0; t < toSave.length; ++t) { - var layers = toSave[t]; - for (var l = 0; l < layers.length; ++l) { - var layer = layers[l]; - var isBoundary = layer.name === ''; - var toLayer = {name : layer.name, data : []}; - sketch.layers.push(toLayer); - for (var i = 0; i < layer.objects.length; ++i) { - var obj = layer.objects[i]; - var to = {id: obj.id, _class: obj._class}; - if (obj.aux) to.aux = obj.aux; - if (obj.edge !== undefined) to.edge = obj.edge; - toLayer.data.push(to); - if (obj._class === 'TCAD.TWO.Segment') { - to.points = [point(obj.a), point(obj.b)]; - } else if (obj._class === 'TCAD.TWO.EndPoint') { - to.location = point(obj); - } else if (obj._class === 'TCAD.TWO.Arc') { - to.points = [point(obj.a), point(obj.b), point(obj.c)]; - } else if (obj._class === 'TCAD.TWO.Circle') { - to.c = point(obj.c); - to.r = obj.r.get(); - } else if (obj._class === 'TCAD.TWO.Dimension' || obj._class === 'TCAD.TWO.HDimension' || obj._class === 'TCAD.TWO.VDimension') { - to.a = obj.a.id; - to.b = obj.b.id; - to.flip = obj.flip; - } - } - } - } - - var constrs = sketch.constraints = []; - var subSystems = app.viewer.parametricManager.subSystems; - for (var j = 0; j < subSystems.length; j++) { - var sub = subSystems[j]; - for (var i = 0; i < sub.constraints.length; ++i) { - if (!sub.constraints[i].aux) { - constrs.push(app.serializeConstr(sub.constraints[i])); - } - } - - } - var sketchData = JSON.stringify(sketch); + var sketchData = app.saveSketch(); console.log(sketchData); - var sketchId = app.getSketchId(); localStorage.setItem(app.getSketchId(), sketchData); }); @@ -182,6 +148,10 @@ TCAD.App2D.prototype.loadFromLocalStorage = function() { var sketchData = localStorage.getItem(sketchId); var boundary = null; if (sketchData != null) { + this.lastCheckpoint = sketchData; + this.diffs = []; + this.historyPointer = -1; + var sketch = JSON.parse(sketchData); boundary = this.loadSketch(sketch); } @@ -201,6 +171,59 @@ TCAD.App2D.prototype.cleanUp = function() { } }; +TCAD.App2D.prototype.saveSketch = function() { + var sketch = {}; + //sketch.boundary = boundary; + sketch.layers = []; + function point(p) { + return [ p.id, [p._x.id, p.x], [p._y.id, p.y] ]; + } + var toSave = [this.viewer.dimLayers, this.viewer.layers] + for (var t = 0; t < toSave.length; ++t) { + var layers = toSave[t]; + for (var l = 0; l < layers.length; ++l) { + var layer = layers[l]; + var isBoundary = layer.name === ''; + var toLayer = {name : layer.name, data : []}; + sketch.layers.push(toLayer); + for (var i = 0; i < layer.objects.length; ++i) { + var obj = layer.objects[i]; + var to = {id: obj.id, _class: obj._class}; + if (obj.aux) to.aux = obj.aux; + if (obj.edge !== undefined) to.edge = obj.edge; + toLayer.data.push(to); + if (obj._class === 'TCAD.TWO.Segment') { + to.points = [point(obj.a), point(obj.b)]; + } else if (obj._class === 'TCAD.TWO.EndPoint') { + to.location = point(obj); + } else if (obj._class === 'TCAD.TWO.Arc') { + to.points = [point(obj.a), point(obj.b), point(obj.c)]; + } else if (obj._class === 'TCAD.TWO.Circle') { + to.c = point(obj.c); + to.r = obj.r.get(); + } else if (obj._class === 'TCAD.TWO.Dimension' || obj._class === 'TCAD.TWO.HDimension' || obj._class === 'TCAD.TWO.VDimension') { + to.a = obj.a.id; + to.b = obj.b.id; + to.flip = obj.flip; + } + } + } + } + + var constrs = sketch.constraints = []; + var subSystems = this.viewer.parametricManager.subSystems; + for (var j = 0; j < subSystems.length; j++) { + var sub = subSystems[j]; + for (var i = 0; i < sub.constraints.length; ++i) { + if (!sub.constraints[i].aux) { + constrs.push(this.serializeConstr(sub.constraints[i])); + } + } + + } + return JSON.stringify(sketch); +}; + TCAD.App2D.prototype.loadSketch = function(sketch) { this.cleanUp(); @@ -385,3 +408,75 @@ TCAD.App2D.prototype.getSketchId = function() { } return "TCAD.projects." + id; }; + +TCAD.App2D.prototype.undo = function () { + var currentState = this.saveSketch(); + if (currentState == this.lastCheckpoint) { + if (this.historyPointer != -1) { + this.loadSketch(JSON.parse(this.lastCheckpoint)); + this.viewer.refresh(); + var diff = this.diffs[this.historyPointer]; + this.lastCheckpoint = this.applyDiff(this.lastCheckpoint, diff); + this.historyPointer --; + } + } else { + var diffToCurr = this.getDiff(this.lastCheckpoint, currentState); + if (this.historyPointer != this.diffs.length - 1) { + this.diffs.splice(this.historyPointer + 1, this.diffs.length - this.historyPointer + 1) + } + this.diffs.push(diffToCurr); + this.loadSketch(JSON.parse(this.lastCheckpoint)); + this.viewer.refresh(); + } +}; + +TCAD.App2D.prototype.checkpoint = function () { + var currentState = this.saveSketch(); + if (currentState == this.lastCheckpoint) { + return; + } + var diffToCurr = this.getDiff(currentState, this.lastCheckpoint); + if (this.historyPointer != this.diffs.length - 1) { + this.diffs.splice(this.historyPointer + 1, this.diffs.length - this.historyPointer + 1) + } + this.diffs.push(diffToCurr); + this.historyPointer = this.diffs.length - 1; + this.lastCheckpoint = currentState; +}; + +TCAD.App2D.prototype.redo = function () { + var currentState = this.saveSketch(); + if (currentState != this.lastCheckpoint) { + return; + } + if (this.historyPointer != this.diffs.length - 1 && this.diffs.length != 0) { + this.historyPointer ++; + var diff = this.diffs[this.historyPointer]; + this.lastCheckpoint = this.applyDiffInv(this.lastCheckpoint, diff); + this.loadSketch(JSON.parse(this.lastCheckpoint)); + } +}; + +TCAD.App2D.prototype.applyDiff = function (text1, diff) { + var dmp = this.dmp; + var results = dmp.patch_apply(diff, text1); + return results[0]; +}; + +TCAD.App2D.prototype.applyDiffInv = function () { + +}; + +TCAD.App2D.prototype.getDiff = function (text1, text2) { + var dmp = this.dmp; + var diff = dmp.diff_main(text1, text2, true); + + if (diff.length > 2) { + dmp.diff_cleanupSemantic(diff); + } + + var patch_list = dmp.patch_make(text1, text2, diff); + var patch_text = dmp.patch_toText(patch_list); + console.log(patch_text); + return patch_list; +}; diff --git a/web/lib/diff_match_patch.js b/web/lib/diff_match_patch.js new file mode 100755 index 00000000..c41b5132 --- /dev/null +++ b/web/lib/diff_match_patch.js @@ -0,0 +1,49 @@ +(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32} +diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a, +b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a}; +diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l= +u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]}; +diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; +diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null; +var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]}; +diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}}; +diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_); +return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]= +h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; +diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)}; +diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; +diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h}; +diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&& +e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g); +if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0, +c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; +diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&& +(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c + @@ -209,7 +210,7 @@ - +