diff --git a/web/app/math/noptim.js b/web/app/math/noptim.js index 78de33d4..8720e33c 100644 --- a/web/app/math/noptim.js +++ b/web/app/math/noptim.js @@ -258,7 +258,7 @@ optim.inv = function inv(x) { } // this is Gauss-Newton least square algorithm with trust region(dog leg) control/ -optim.dog_leg = function(subsys) { +optim.dog_leg = function(subsys, rough) { var tolg=1e-80, tolx=1e-80, tolf=1e-10; @@ -361,7 +361,7 @@ optim.dog_leg = function(subsys) { while (stop === 0) { // check if finished - if (fx_inf <= tolf || err <= tolf) // Success + if (fx_inf <= tolf || (rough && err <= tolf)) // Success stop = 1; else if (g_inf <= tolg) stop = 2; @@ -472,8 +472,11 @@ optim.dog_leg = function(subsys) { iter++; } - - return (stop == 1) ? 'Success' : 'Failed'; + return { + evalCount : iter, + error : err, + returnCode : stop + }; }; diff --git a/web/app/sketcher/canvas.js b/web/app/sketcher/canvas.js index 41d88801..8b5d0952 100644 --- a/web/app/sketcher/canvas.js +++ b/web/app/sketcher/canvas.js @@ -780,7 +780,7 @@ TCAD.TWO.DragTool.prototype.mousemove = function(e) { var checkY = this.ref.y; this.obj.translate(dx, dy); if (!e.shiftKey) { - this.solveRequest(2); + this.solveRequest(true); } this.errorX = (this.ref.x - dx) - checkX; @@ -798,7 +798,7 @@ TCAD.TWO.DragTool.prototype.mousedown = function(e) { }; TCAD.TWO.DragTool.prototype.mouseup = function(e) { - this.solveRequest(0); + this.solveRequest(false); this.viewer.refresh(); this.viewer.toolManager.releaseControl(); }; @@ -806,7 +806,7 @@ TCAD.TWO.DragTool.prototype.mouseup = function(e) { TCAD.TWO.DragTool.prototype.mousewheel = function(e) { }; -TCAD.TWO.DragTool.prototype.solveRequest = function(fineLevel) { +TCAD.TWO.DragTool.prototype.solveRequest = function(rough) { var locked; if (this.obj._class === 'TCAD.TWO.EndPoint') { @@ -823,7 +823,7 @@ TCAD.TWO.DragTool.prototype.solveRequest = function(fineLevel) { locked = []; } this.solver = this.viewer.parametricManager.prepare(locked); - this.solver.solve(fineLevel); + this.solver.solve(rough, 1); this.solver.sync(); }; diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index e7cd8f27..e974deff 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -205,8 +205,6 @@ TCAD.parametric.lock2Equals2 = function(constrs, locked) { return _locked; }; -TCAD.parametric._alg = 5; - TCAD.parametric.diagnose = function(sys) { var jacobian = sys.makeJacobian(); var qr = new TCAD.math.QR(jacobian); @@ -224,15 +222,6 @@ TCAD.parametric.prepare = function(constrs, locked, aux, alg) { var sys = new TCAD.parametric.System(constrs); - function arr(size) { - var out = []; - out.length = size; - for (var i = 0; i < size; ++i) { - out[i] = 0; - } - return out; - } - var model = function(point) { sys.setParams(point); return sys.getValues(); @@ -242,62 +231,22 @@ TCAD.parametric.prepare = function(constrs, locked, aux, alg) { sys.setParams(point); return sys.makeJacobian(); }; - alg = TCAD.parametric._alg; - var _point = []; - function solve(fineLevel) { + function solve(rough, alg) { if (constrs.length == 0) return; if (sys.params.length == 0) return; if (TCAD.parametric.diagnose(sys).conflict) { console.log("Conflicting or redundant constraints. Please fix your system."); return; } - if (alg > 0) { - switch (alg) { - case 1: - var res = TCAD.math.solve_BFGS(sys, 1e-4, 1e-4); - console.log(res); - break; - case 2: - TCAD.math.solve_TR(sys); - break; - case 3: - TCAD.math.noptim(sys); - break; - case 4: - TCAD.math.solve_UNCMIN(sys); - break; - case 5: - if (optim.dog_leg(sys) !== 0) { - //alg = -5; - //solve(fineLevel); - } - break; - } - return sys; - } - - var opt = new LMOptimizer(sys.getParams(), arr(sys.constraints.length), model, jacobian); - var eps; - fineLevel = 1; - switch (fineLevel) { - case 1: - eps = 0.001; - opt.init0(eps, eps, eps); - break; + + switch (alg) { case 2: - eps = 0.1; - opt.init0(eps, eps, eps); - break; - default: - eps = 0.00000001; - opt.init0(eps, eps, eps); + return TCAD.parametric.solve_lm(sys, model, jacobian, rough); + case 1: + default: + return optim.dog_leg(sys, rough); } - - var res = opt.doOptimize(); - sys.setParams(res[0]); - // console.log("Solved with error: " + sys.error()); - return res; } var systemSolver = { system : sys, @@ -308,7 +257,25 @@ TCAD.parametric.prepare = function(constrs, locked, aux, alg) { } } }; - return systemSolver; - }; + +TCAD.parametric.solve_lm = function(sys, model, jacobian, rough) { + var opt = new LMOptimizer(sys.getParams(), TCAD.math.vec(sys.constraints.length), model, jacobian); + opt.evalMaximalCount = 100 * sys.params.length; + var eps = rough ? 0.001 : 0.00000001; + opt.init0(eps, eps, eps); + var returnCode = 1; + try { + var res = opt.doOptimize(); + } catch (e) { + returnCode = 2; + } + sys.setParams(res[0]); + return { + evalCount : opt.evalCount, + error : sys.error(), + returnCode : returnCode + }; +}; + diff --git a/web/app/sketcher/main2d.js b/web/app/sketcher/main2d.js index 455fd0b6..60efa1a7 100644 --- a/web/app/sketcher/main2d.js +++ b/web/app/sketcher/main2d.js @@ -88,11 +88,15 @@ TCAD.App2D = function() { } var constrs = sketch.constraints = []; - var sys = app.viewer.parametricManager.system; - for (var i = 0; i < sys.length; ++i) { - if (!sys[i].aux) { - constrs.push(app.serializeConstr(sys[i])); + 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); console.log(sketchData); @@ -150,17 +154,7 @@ TCAD.App2D = function() { }); this.registerAction('solve', "Solve System", function () { - app.viewer.parametricManager.solve([], 0); - app.viewer.refresh(); - }); - - this.registerAction('solveStep', "Solve Step", function () { - app.viewer.parametricManager.solve([], 0, 3); - app.viewer.refresh(); - }); - - this.registerAction('stepUNCMIN', "Solve Step UNCMIN", function () { - app.viewer.parametricManager.solve([], 0, 4); + app.viewer.parametricManager.solve(); app.viewer.refresh(); }); @@ -289,7 +283,7 @@ TCAD.App2D.prototype.loadSketch = function(sketch) { if (sketch.constraints !== undefined) { for (var i = 0; i < sketch.constraints.length; ++i) { var c = this.parseConstr(sketch.constraints[i], index); - this.viewer.parametricManager.system.push(c); + this.viewer.parametricManager._add(c); } this.viewer.parametricManager.notify(); } diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 1c05a6cb..88e95e8f 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -1,9 +1,15 @@ TCAD.TWO.Constraints = {}; +TCAD.TWO.SubSystem = function() { + this.alg = 1; + this.error = 0; + this.reduce = false; + this.constraints = []; +}; + TCAD.TWO.ParametricManager = function(viewer) { this.viewer = viewer; - this.system = []; - this.REQUEST_COUNTER = 0; + this.subSystems = []; this.listeners = []; }; @@ -14,27 +20,74 @@ TCAD.TWO.ParametricManager.prototype.notify = function(event) { } }; -TCAD.TWO.ParametricManager.prototype.add = function(constr) { - this.system.push(constr); - this.solve([], 0.00000001, 5); +TCAD.TWO.ParametricManager.prototype.findComponents = function(constr) { + if (this.subSystems.length === 0) { + this.subSystems.push(new TCAD.TWO.SubSystem()); + } + return [0]; +}; + +TCAD.TWO.ParametricManager.prototype.tune = function(subSystem) { + +}; + +TCAD.TWO.ParametricManager.prototype._add = function(constr) { + var subSystemIds = this.findComponents(constr); + var subSystem; + switch (subSystemIds.length) { + case 0: + subSystem = new TCAD.TWO.SubSystem(); + this.subSystems.push(subSystem); + break; + case 1: + subSystem = this.subSystems[subSystemIds[0]]; + break; + default: + subSystem = this.subSystems[subSystemIds[0]]; + for (var i = 1; i < subSystemIds.length; i++) { + var toMerge = subSystemIds[i]; + for (var j = 0; j < toMerge.constraints.length; j++) { + subSystem.push(toMerge.constraints[j]); + } + } + break; + } + subSystem.constraints.push(constr); +}; + +TCAD.TWO.ParametricManager.prototype.refresh = function() { + this.solve(); this.notify(); this.viewer.refresh(); }; +TCAD.TWO.ParametricManager.prototype.add = function(constr) { + this._add(constr); + this.refresh(); +}; + +TCAD.TWO.ParametricManager.prototype.addAll = function(constrs) { + for (var i = 0; i < constrs.length; i++) { + this._add(constrs[i]); + } + this.refresh(); +}; + TCAD.TWO.ParametricManager.prototype.remove = function(constr) { - for (var i = 0; i < this.system.length; ++i) { - var p = this.system[i]; - if (p === constr) { - this.system.splice(i, 1); - if (p.NAME === 'coi') { - this.unlinkObjects(p.a, p.b); + for (var j = 0; j < this.subSystems.length; j++) { + var sub = this.subSystems[j]; + for (var i = 0; i < sub.constraints.length; ++i) { + var p = sub.constraints[i]; + if (p === constr) { + sub.constraints(i, 1); + if (p.NAME === 'coi') { + this.unlinkObjects(p.a, p.b); + } + break; } - break; } } - this.solve(); - this.notify(); - this.viewer.refresh(); + this.refresh(); }; TCAD.TWO.ParametricManager.prototype.removeConstraintsByObj = function(obj) { @@ -44,40 +97,41 @@ TCAD.TWO.ParametricManager.prototype.removeConstraintsByObj = function(obj) { }; TCAD.TWO.ParametricManager.prototype.removeConstraintsByParams = function(ownedParams) { - var toRemove = []; - for (var i = 0; i < this.system.length; ++i) { - var sdataArr = this.system[i].getSolveData(); - for (var j = 0; j < sdataArr.length; j++) { - var sdata = sdataArr[j]; - var params = sdata[1]; - MAIN: - for (var j = 0; j < ownedParams.length; ++j) { - for (var k = 0; k < params.length; ++k) { - if (ownedParams[j].id === params[k].id) { - toRemove.push(i); - break MAIN; + for (var s = 0; s < this.subSystems.length; s++) { + var toRemove = []; + var sub = this.subSystems[s]; + for (var i = 0; i < sub.constraints.length; ++i) { + var sdataArr = sub.constraints[i].getSolveData(); + for (var j = 0; j < sdataArr.length; j++) { + var sdata = sdataArr[j]; + var params = sdata[1]; + MAIN: + for (var oi = 0; oi < ownedParams.length; ++oi) { + for (var k = 0; k < params.length; ++k) { + if (ownedParams[oi].id === params[k].id) { + toRemove.push(i); + break MAIN; + } } } } } + toRemove.sort(); + + for (var i = toRemove.length - 1; i >= 0 ; --i) { + sub.constraints.splice( toRemove[i], 1); + } } - toRemove.sort(); - - for (var i = toRemove.length - 1; i >= 0 ; --i) { - this.system.splice( toRemove[i], 1); - } this.notify(); }; TCAD.TWO.ParametricManager.prototype.lock = function(objs) { var p = this._fetchPoints(objs); for (var i = 0; i < p.length; ++i) { - this.system.push(new TCAD.TWO.Constraints.Lock(p[i], { x : p[i].x, y : p[i].y} )); + this._add(new TCAD.TWO.Constraints.Lock(p[i], { x : p[i].x, y : p[i].y} )); } - this.solve(); - this.notify(); - this.viewer.refresh(); + this.refresh(); }; TCAD.TWO.ParametricManager.prototype.vertical = function(objs) { @@ -109,12 +163,10 @@ TCAD.TWO.ParametricManager.prototype.rr = function(objs) { var arcs = this._fetchArkCirc(objs, 2); var prev = arcs[0]; for (var i = 1; i < arcs.length; ++i) { - this.system.push(new TCAD.TWO.Constraints.RR(prev, arcs[i])); + this._add(new TCAD.TWO.Constraints.RR(prev, arcs[i])); prev = arcs[i]; } - this.solve(); - this.notify(); - this.viewer.refresh(); + this.refresh(); }; TCAD.TWO.ParametricManager.prototype.p2lDistance = function(objs, promptCallback) { @@ -164,11 +216,9 @@ TCAD.TWO.ParametricManager.prototype.radius = function(objs, promptCallback) { promptDistance = Number(promptDistance); if (promptDistance == promptDistance) { // check for NaN for (var i = 0; i < arcs.length; ++i) { - this.system.push(new TCAD.TWO.Constraints.Radius(arcs[i], promptDistance)); + this._add(new TCAD.TWO.Constraints.Radius(arcs[i], promptDistance)); } - this.solve(); - this.notify(); - this.viewer.refresh(); + this.refresh(); } } }; @@ -180,9 +230,8 @@ TCAD.TWO.ParametricManager.prototype.linkObjects = function(objs) { objs[i].x = objs[last].x; objs[i].y = objs[last].y; var c = new TCAD.TWO.Constraints.Coincident(objs[i], objs[last]); - this.system.push(c); + this._add(c); } - this.notify(); }; @@ -209,82 +258,65 @@ TCAD.TWO.ParametricManager.prototype.coincident = function(objs) { }; TCAD.TWO.ParametricManager.prototype.getSolveData = function() { - var sdata = []; - for (i = 0; i < this.system.length; ++i) { - var data = this.system[i].getSolveData(); - for (var j = 0; j < data.length; ++j) { - data[j].push(this.system[i].reducible !== undefined); - sdata.push(data[j]); - } + var sdata = []; + for (var i = 0; i < this.subSystems.length; i++) { + this.__getSolveData(this.subSystems[i], sdata); } return sdata; }; -TCAD.TWO.ParametricManager.prototype.solve1 = function(locked, onSolved) { - var pdict = {}; - var refsCounter = 0; - var params = []; - var i; - var data = {params : [], constraints: [], locked: []}; - var sdataArr = this.getSolveData(); - for (var j = 0; j < sdataArr.length; j++) { - var sdata = sdataArr[j]; - var prefs = []; - var constr = [sdata[0], prefs, sdata[2]]; - data.constraints.push(constr); - for (var p = 0; p < sdata[1].length; ++p) { - var param = sdata[1][p]; - var pref = pdict[param.id]; - if (pref === undefined) { - pref = refsCounter++; - data.params.push(param.get()); - params.push(param); - pdict[param.id] = pref; - } - prefs.push(pref); +TCAD.TWO.ParametricManager.prototype.__getSolveData = function(subSystem, out) { + for (var i = 0; i < subSystem.constraints.length; ++i) { + var constraint = subSystem.constraints[i]; + var data = constraint.getSolveData(); + for (var j = 0; j < data.length; ++j) { + data[j].push(constraint.reducible !== undefined); + out.push(data[j]); } } + return out; +}; - if (locked !== undefined) { - for (i = 0; i < locked.length; ++i) { - var lp = pdict[locked[i].id]; - if (lp !== undefined) { - data.locked.push(lp); +TCAD.TWO.ParametricManager.prototype.solve = function() { + var solver = this.prepare([]); + solver.solve(false); + solver.sync(); +}; + +TCAD.TWO.ParametricManager.prototype.prepare = function(locked) { + var solvers = []; + for (var i = 0; i < this.subSystems.length; i++) { + solvers.push(this._prepare(locked, this.subSystems[i])); + } + var subSystems = this.subSystems; + return { + solve : function(rough) { + for (var i = 0; i < solvers.length; i++) { + var alg = subSystems[i].alg; + if (solvers[i].solve(rough, alg) !== 1) { + alg = alg == 1 ? 2 : 1; + if (solvers[i].solve(rough, alg) == 1) { + subSystems[i].alg = alg; + } + } + } + }, + + sync : function() { + for (var i = 0; i < solvers.length; i++) { + solvers[i].sync(); + } + }, + + updateLock : function() { + for (var i = 0; i < solvers.length; i++) { + solvers[i].updateLock(); } } } - - var xhr = new XMLHttpRequest(); - xhr.withCredentials = true; - var pm = this; - var request = {reqId : this.REQUEST_COUNTER ++, system : data}; - xhr.onreadystatechange=function() { - if (xhr.readyState == 4 && xhr.status == 200) { - var response = JSON.parse(xhr.responseText); - if (response.reqId != pm.REQUEST_COUNTER - 1) { - return; - } - for (var p = 0; p < response.params.length; ++p) { - params[p].set(response.params[p]); - } - if (onSolved !== undefined) { - onSolved(); - } - pm.viewer.refresh(); - } - }; - xhr.open("POST", "http://localhost:8080/solve", true); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.send(JSON.stringify(request)); }; -TCAD.TWO.ParametricManager.prototype.solve = function(locked, fineLevel, alg) { - var solver = this.prepare(locked, alg); - solver.solve(fineLevel); - solver.sync() -}; - -TCAD.TWO.ParametricManager.prototype.prepare = function(locked, alg) { +TCAD.TWO.ParametricManager.prototype._prepare = function(locked, subSystem) { var pdict = {}; var params; @@ -298,7 +330,7 @@ TCAD.TWO.ParametricManager.prototype.prepare = function(locked, alg) { function peq(p1, p2) { return Math.abs(p1.get() - p2.get()) <= 0.000001 } - var system = this.getSolveData(); + var system = this.__getSolveData(subSystem, []); var tuples = []; for (i = 0; i < system.length; ++i) { var c = system[i]; @@ -416,7 +448,7 @@ TCAD.TWO.ParametricManager.prototype.prepare = function(locked, alg) { var _constr = TCAD.constraints.create(sdata[0], params, sdata[2]); _constrs.push(_constr); if (sdata[0] === 'equal') { - equals.push(this.system[i]); + equals.push(system[i]); } } @@ -430,8 +462,8 @@ TCAD.TWO.ParametricManager.prototype.prepare = function(locked, alg) { } var lockedValues = []; - var solver = TCAD.parametric.prepare(_constrs, _locked, aux, alg); - function solve(fineLevel) { + 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() ; @@ -442,7 +474,7 @@ TCAD.TWO.ParametricManager.prototype.prepare = function(locked, alg) { _p = pdict[p]; _p.set(_p._backingParam.get()); } - solver.solveSystem(fineLevel); + solver.solveSystem(rough, alg); } var viewer = this.viewer; function sync() { diff --git a/web/app/sketcher/shapes/arc.js b/web/app/sketcher/shapes/arc.js index 6f0bdbb8..436a81bc 100644 --- a/web/app/sketcher/shapes/arc.js +++ b/web/app/sketcher/shapes/arc.js @@ -67,8 +67,8 @@ TCAD.TWO.Arc.prototype.normalDistance = function(aim) { TCAD.TWO.Arc.prototype.stabilize = function(viewer) { this.r.set(this.distanceA()); - viewer.parametricManager.system.push(new TCAD.TWO.Constraints.P2PDistanceV(this.b, this.c, this.r)); - viewer.parametricManager.system.push(new TCAD.TWO.Constraints.P2PDistanceV(this.a, this.c, this.r)); + viewer.parametricManager._add(new TCAD.TWO.Constraints.P2PDistanceV(this.b, this.c, this.r)); + viewer.parametricManager._add(new TCAD.TWO.Constraints.P2PDistanceV(this.a, this.c, this.r)); }; TCAD.TWO.AddArcTool = function(viewer, layer) { diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index 8bb69d74..6c03064b 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -58,15 +58,15 @@ TCAD.TWO.EditCircleTool.prototype.mousemove = function(e) { var r = TCAD.math.distance(p.x, p.y, this.circle.c.x, this.circle.c.y); this.circle.r.set(r); if (!e.shiftKey) { - this.solveRequest(2); + this.solveRequest(true); } this.viewer.refresh(); } }; -TCAD.TWO.EditCircleTool.prototype.solveRequest = function(fineLevel) { +TCAD.TWO.EditCircleTool.prototype.solveRequest = function(rough) { this.solver = this.viewer.parametricManager.prepare([this.circle.r]); - this.solver.solve(fineLevel); + this.solver.solve(rough, 1); this.solver.sync(); }; @@ -79,7 +79,7 @@ TCAD.TWO.EditCircleTool.prototype.mouseup = function(e) { this.layer.objects.push(this.circle); this.viewer.refresh(); } else { - this.solveRequest(0); + this.solveRequest(false); this.viewer.refresh(); this.viewer.toolManager.releaseControl(); } diff --git a/web/sketcher.html b/web/sketcher.html index 2e00ac34..91a718e0 100644 --- a/web/sketcher.html +++ b/web/sketcher.html @@ -227,10 +227,13 @@ var constrList = new TCAD.ui.List($('#constrs'), { items : function() { var theItems = []; - for (var i = 0; i < pm.system.length; ++i) { - var constr = pm.system[i]; - if (constr.aux !== true) { - theItems.push({name : constr.NAME, constr : constr}); + for (var j = 0; j < pm.subSystems.length; j++) { + var sub = pm.subSystems[j]; + for (var i = 0; i < sub.constraints.length; ++i) { + var constr = sub.constraints[i]; + if (constr.aux !== true) { + theItems.push({name : constr.NAME, constr : constr}); + } } } return theItems;