diff --git a/web/app/math/optim.js b/web/app/math/optim.js index f3804222..336a7f02 100644 --- a/web/app/math/optim.js +++ b/web/app/math/optim.js @@ -1,5 +1,7 @@ optim = {}; +optim.DEBUG_HANDLER = function() {}; + //Added strong wolfe condition to numeric's uncmin optim.bfgs_ = function(f,x0,tol,gradient,maxit,callback,options) { var grad = numeric.gradient; @@ -335,6 +337,7 @@ optim.dog_leg = function (subsys, rough) { var iter = 0, stop = 0, reduce = 0; //var log = []; while (stop === 0) { + optim.DEBUG_HANDLER(iter, err); // check if finished if (fx_inf <= tolf ) // Success @@ -365,14 +368,12 @@ optim.dog_leg = function (subsys, rough) { var hitBoundary = false; - var stepKind; // compute the dogleg step var gnorm = n.norm2(g); - if (n.norm2(h_gn) < delta) { + var gnNorm = n.norm2(h_gn); + if (gnNorm < delta) { h_dl = h_gn; - stepKind = 1; - } - else { + } else { var Jt = n.transpose(Jx); var B = n.dot(Jt, Jx); var gBg = n.dot(g, n.dot(B, g)); @@ -380,24 +381,26 @@ optim.dog_leg = function (subsys, rough) { if (alpha * gnorm >= delta) { h_dl = n.mul(g, - delta / gnorm); hitBoundary = true; - stepKind = 2; } else { var h_sd = n.mul(g, - alpha); + if (isNaN(gnNorm)) { + h_dl = h_sd; + } else { - var d = n.sub(h_gn, h_sd); + var d = n.sub(h_gn, h_sd); - var a = n.dot(d, d); - var b = 2 * n.dot(h_sd, d); - var c = n.dot(h_sd, h_sd) - delta * delta + var a = n.dot(d, d); + var b = 2 * n.dot(h_sd, d); + var c = n.dot(h_sd, h_sd) - delta * delta; - var sqrt_discriminant = Math.sqrt(b * b - 4 * a * c) + var sqrt_discriminant = Math.sqrt(b * b - 4 * a * c); - var beta = (-b + sqrt_discriminant) / (2 * a) + var beta = (-b + sqrt_discriminant) / (2 * a); - // and update h_dl and dL with beta - h_dl = n.add(h_sd, n.mul(beta, d)); - hitBoundary = true; - stepKind = 3; + // and update h_dl and dL with beta + h_dl = n.add(h_sd, n.mul(beta, d)); + hitBoundary = true; + } } } } diff --git a/web/app/sketcher/canvas.js b/web/app/sketcher/canvas.js index ff759fde..08edf12c 100644 --- a/web/app/sketcher/canvas.js +++ b/web/app/sketcher/canvas.js @@ -839,11 +839,9 @@ TCAD.TWO.DragTool = function(obj, viewer) { this._point = {x: 0, y: 0}; this.origin = {x: 0, y: 0}; this.ref = this.obj.getReferencePoint(); - this.errorX = 0; - this.errorY = 0; this.solver = null; }; - +TCAD.TWO.DragTool.prototype.snapshots = [] TCAD.TWO.DragTool.prototype.keydown = function(e) {}; TCAD.TWO.DragTool.prototype.keypress = function(e) {}; TCAD.TWO.DragTool.prototype.keyup = function(e) {}; @@ -852,29 +850,27 @@ TCAD.TWO.DragTool.prototype.cleanup = function(e) {}; TCAD.TWO.DragTool.prototype.mousemove = function(e) { var x = this._point.x; var y = this._point.y; - this.viewer.screenToModel2(e.x, e.y, this._point); - var dx = this._point.x - x - this.errorX; - var dy = this._point.y - y - this.errorY; - var checkX = this.ref.x; - var checkY = this.ref.y; - this.obj.translate(dx, dy); + this.viewer.screenToModel2(e.offsetX, e.offsetY, this._point); + var dx = this._point.x - x; + var dy = this._point.y - y; if (!e.altKey && !e.ctrlKey) { + for (var i = 0; i < this.lockedShifts.length; i += 2) { + this.lockedValues[i] = this._point.x - this.lockedShifts[i]; + this.lockedValues[i + 1] = this._point.y - this.lockedShifts[i + 1]; + } + this.solver.updateLock(this.lockedValues); this.solveRequest(true); + } else { + this.obj.translate(dx, dy); } - this.errorX = (this.ref.x - dx) - checkX; - this.errorY = (this.ref.y - dy) - checkY; - -// console.log("accumulated error X = " + this.errorX); -// console.log("accumulated error Y = " + this.errorY); - this.viewer.refresh(); }; TCAD.TWO.DragTool.prototype.mousedown = function(e) { - this.origin.x = e.x; - this.origin.y = e.y; - this.viewer.screenToModel2(e.x, e.y, this._point); + this.origin.x = e.offsetX; + this.origin.y = e.offsetY; + this.viewer.screenToModel2(e.offsetX, e.offsetY, this._point); this.prepareSolver(); }; @@ -882,10 +878,11 @@ TCAD.TWO.DragTool.prototype.mouseup = function(e) { this.solveRequest(false); this.viewer.refresh(); this.viewer.toolManager.releaseControl(); - var traveled = TCAD.math.distance(this.origin.x, this.origin.y, e.x, e.y); + var traveled = TCAD.math.distance(this.origin.x, this.origin.y, e.offsetX, e.offsetY); if (traveled >= 10) { this.viewer.historyManager.lightCheckpoint(10); } + //this.animateSolution(); }; TCAD.TWO.DragTool.prototype.mousewheel = function(e) { @@ -896,20 +893,70 @@ TCAD.TWO.DragTool.prototype.solveRequest = function(rough) { this.solver.sync(); }; +TCAD.TWO.DragTool.prototype.getParamsToLock = function() { + var params = []; + this.obj.accept(function(obj) { + if (obj._class === 'TCAD.TWO.EndPoint') { + params.push(obj._x); + params.push(obj._y); + } + return true; + }); + return params; +}; + TCAD.TWO.DragTool.prototype.prepareSolver = function() { - var locked; - if (this.obj._class === 'TCAD.TWO.EndPoint') { - locked = [this.obj._x, this.obj._y]; -// if (this.obj.parent != null -// && this.obj.parent._class === 'TCAD.TWO.Arc') { -// -// if (this.obj.id != this.obj.parent.c.id) { -// locked.push(this.obj.parent.c._x); -// locked.push(this.obj.parent.c._y); -// } -// } - } else { - locked = []; + var locked = this.getParamsToLock(); + this.lockedShifts = []; + this.lockedValues = []; + for (var i = 0; i < locked.length; i += 2) { + this.lockedShifts[i] = this._point.x - locked[i].get(); + this.lockedShifts[i + 1] = this._point.y - locked[i + 1].get(); } this.solver = this.viewer.parametricManager.prepare(locked); + //this.enableRecording(); }; + +TCAD.TWO.DragTool.prototype.enableRecording = function() { + var solver = this.solver; + var snapshots = this.snapshots = []; + optim.DEBUG_HANDLER = function() { + snapshots.push([]); + for (var i = 0; i < solver.solvers.length; i++) { + var sys = solver.solvers[i].system; + snapshots[i].push(sys.params.map(function(p) {return p.get()})) + } + }; +}; + +TCAD.TWO.DragTool.prototype.animateSolution = function() { + if (this.snapshots.length === 0) return; + var stepNum = 0; + var scope = this; + var then = Date.now(); + var speed = 3000; + function step() { + var now = Date.now(); + var elapsed = now - then; + + if (elapsed > speed) { + for (var i = 0; i < scope.solver.solvers.length; i++) { + var sys = scope.solver.solvers[i].system; + if (stepNum >= scope.snapshots[i].length) continue; + var values = scope.snapshots[i][stepNum]; + for (var k = 0; k < values.length; k++) { + sys.params[k]._backingParam.set(values[k]); + } + } + stepNum ++; + + then = now; + scope.viewer.repaint(); + } + + if (stepNum < scope.snapshots[0].length) { + window.requestAnimationFrame(step); + } + } + window.requestAnimationFrame(step); +}; \ No newline at end of file diff --git a/web/app/sketcher/constr/constraints.js b/web/app/sketcher/constr/constraints.js index 603fd6f8..6b21206d 100644 --- a/web/app/sketcher/constr/constraints.js +++ b/web/app/sketcher/constr/constraints.js @@ -236,6 +236,7 @@ TCAD.constraints.P2PDistance = function(params, distance) { out[p1y] = dy / d; out[p2x] = -dx / d; out[p2y] = -dy / d; + for (var i = 0; i < 4; i++) if (Number.isNaN(out[i])) out[i] = 0; } }; @@ -267,6 +268,7 @@ TCAD.constraints.P2PDistanceV = function(params) { out[p2x] = -dx / d; out[p2y] = -dy / d; out[D] = -1; + for (var i = 0; i < 4; i++) if (Number.isNaN(out[i])) out[i] = 0; } }; diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index db45162a..2af062e3 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -250,7 +250,7 @@ TCAD.parametric.prepare = function(constrs, locked, aux, alg) { returnCode : 1 }; - var conflict = TCAD.parametric.diagnose(sys).conflict; + var conflict = false;//TCAD.parametric.diagnose(sys).conflict; if (conflict) { console.log("Conflicting or redundant constraints. Please fix your system."); } @@ -271,6 +271,7 @@ TCAD.parametric.prepare = function(constrs, locked, aux, alg) { diagnose : function() {return TCAD.parametric.diagnose(sys)}, error : function() {return sys.error()}, solveSystem : solve, + system : sys, updateLock : function(values) { for (var i = 0; i < values.length; ++i) { lockingConstrs[i].constr.value = values[i]; diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 9932e080..e7a0f88a 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -60,7 +60,7 @@ TCAD.TWO.ParametricManager.prototype._add = function(constr) { }; TCAD.TWO.ParametricManager.prototype.checkRedundancy = function (subSystem, constr) { - var solver = this.prepareForSubSystem([], subSystem); + var solver = this.prepareForSubSystem([], subSystem.constraints); if (solver.diagnose().conflict) { alert("Most likely this "+constr.NAME + " constraint is CONFLICTING!") } @@ -295,14 +295,14 @@ TCAD.TWO.ParametricManager.prototype.coincident = function(objs) { TCAD.TWO.ParametricManager.prototype.getSolveData = function() { var sdata = []; for (var i = 0; i < this.subSystems.length; i++) { - this.__getSolveData(this.subSystems[i], sdata); + this.__getSolveData(this.subSystems[i].constraints, sdata); } return sdata; }; -TCAD.TWO.ParametricManager.prototype.__getSolveData = function(subSystem, out) { - for (var i = 0; i < subSystem.constraints.length; ++i) { - var constraint = subSystem.constraints[i]; +TCAD.TWO.ParametricManager.prototype.__getSolveData = function(constraints, out) { + for (var i = 0; i < constraints.length; ++i) { + var constraint = constraints[i]; var data = constraint.getSolveData(); for (var j = 0; j < data.length; ++j) { data[j].push(constraint.reducible !== undefined); @@ -324,21 +324,24 @@ TCAD.TWO.ParametricManager.prototype.solveWithLock = function(lock) { solver.sync(); }; -TCAD.TWO.ParametricManager.prototype.prepare = function(locked) { - return this._prepare(locked, this.subSystems); +TCAD.TWO.ParametricManager.prototype.prepare = function(locked, extraConstraints) { + return this._prepare(locked, this.subSystems, extraConstraints); }; -TCAD.TWO.ParametricManager.prototype._prepare = function(locked, subSystems) { +TCAD.TWO.ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints) { var solvers = []; for (var i = 0; i < subSystems.length; i++) { - solvers.push(this.prepareForSubSystem(locked, subSystems[i])); + solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints)); + } + if (subSystems.length == 0 && locked.length != 0) { + solvers.push(this.prepareForSubSystem(locked, [], extraConstraints)); } return { solvers : solvers, solve : function(rough) { for (var i = 0; i < solvers.length; i++) { - var alg = subSystems[i].alg; + var alg = i < subSystems.length ? subSystems[i].alg : 1; var res = solvers[i].solve(rough, alg); if (res.returnCode !== 1) { alg = alg == 1 ? 2 : 1; @@ -354,10 +357,10 @@ TCAD.TWO.ParametricManager.prototype._prepare = function(locked, subSystems) { solvers[i].sync(); } }, - - updateLock : function() { + + updateLock : function(values) { for (var i = 0; i < solvers.length; i++) { - solvers[i].updateLock(); + solvers[i].updateLock(values); } } } @@ -393,7 +396,7 @@ TCAD.TWO.ParametricManager.__toId = function(v) { return v.id; }; -TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subSystem) { +TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemConstraints, extraConstraints) { var pdict = {}; var params; @@ -401,12 +404,17 @@ TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subS var equalsDict = {}; var equalsIndex = []; - var eqcElimination = []; + var eqcElimination = {}; + + var lockedIds = locked.map(function(p) {return p.id}); function peq(p1, p2) { return Math.abs(p1.get() - p2.get()) <= 0.000001 } - var system = this.__getSolveData(subSystem, []); + var system = []; + this.__getSolveData(subSystemConstraints, system); + if (!!extraConstraints) this.__getSolveData(extraConstraints, system); + // system.sort(function(a, b){ // a = a[0] === 'equal' ? 1 : 2; // b = b[0] === 'equal' ? 1 : 2; @@ -417,18 +425,49 @@ TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subS var tuples = []; if (TCAD.EQUALS_ELIMINATION_ENABLED) { + var c, pi, paramIndex = {}; + + function intersect(array1, array2) { + if (!array1 || !array2) return false; + return array1.filter(function(n) { + return array2.indexOf(n) != -1 + }).length != 0; + } + for (i = 0; i < system.length; ++i) { - var c = system[i]; + c = system[i]; + if (c[3] !== true) { + var sameParams = {}; + for (pi = 0; pi < c[1].length; pi++) { + var param = c[1][pi]; + var paramConstrs = paramIndex[param.id]; + if (paramConstrs === undefined) { + paramConstrs = []; + paramIndex[param.id] = paramConstrs; + } + paramConstrs.push(i); + } + } + } + + for (i = 0; i < system.length; ++i) { + c = system[i]; if (c[3] === true) { //Reduce flag + var cp1 = c[1][0]; var cp2 = c[1][1]; - //if (!peq(cp1, cp2)) continue; var p0 = cp1.id; var p1 = cp2.id; - eqcElimination.push(i); + + var assoc0 = paramIndex[p0]; + var assoc1 = paramIndex[p1]; + if (intersect(assoc0, assoc1)) { + continue; + } equalsDict[p0] = cp1; equalsDict[p1] = cp2; tuples.push([p0, p1]); + eqcElimination[i] = true; } } } @@ -518,14 +557,10 @@ TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subS var _p; var ei; - var ii = 0; var aux = []; for (i = 0; i < system.length; ++i) { - if (eqcElimination[ii] === i) { - ii++; - continue; - } + if (eqcElimination[i] === true) continue; var sdata = system[i]; params = []; @@ -542,23 +577,14 @@ TCAD.TWO.ParametricManager.prototype.prepareForSubSystem = function(locked, subS } var _locked = []; - var lockedIds = {}; if (locked !== undefined) { for (p = 0; p < locked.length; ++p) { _locked[p] = getParam(locked[p]); - lockedIds[locked[p]] = true; } } - var lockedValues = []; var solver = TCAD.parametric.prepare(_constrs, _locked, aux); function solve(rough, alg) { - if (_locked.length != 0) { - for (p = 0; p < locked.length; ++p) { - lockedValues[p] = locked[p].get() ; - } - solver.updateLock(lockedValues); - } for (p in pdict) { _p = pdict[p]; _p.set(_p._backingParam.get());