From d9e61e273e7f18d42545eadeefc02221e8b440f7 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Mon, 2 Jan 2017 19:17:00 -0800 Subject: [PATCH] offset tool --- web/app/sketcher/constr/constraints.js | 30 ++++++++- web/app/sketcher/constr/solver.js | 42 ++++++------ web/app/sketcher/parametric.js | 89 ++++++++++++++++++-------- web/app/sketcher/tools/fillet.js | 4 +- web/app/sketcher/tools/offset.js | 47 ++++++++------ 5 files changed, 140 insertions(+), 72 deletions(-) diff --git a/web/app/sketcher/constr/constraints.js b/web/app/sketcher/constr/constraints.js index 15dc479b..f4dc69b7 100644 --- a/web/app/sketcher/constr/constraints.js +++ b/web/app/sketcher/constr/constraints.js @@ -17,6 +17,8 @@ function createByConstraintName(name, params, values) { return new Perpendicular(params); case "parallel": return new Parallel(params); + case "P2LDistanceSigned": + return new P2LDistanceSigned(params, values[0]); case "P2LDistance": return new P2LDistance(params, values[0]); case "P2LDistanceV": @@ -140,9 +142,8 @@ function ConstantWrapper(constr, mask) { this.params = []; this.grad = []; - var j; - for (j = 0; j < constr.params.length; j++) { + for (let j = 0; j < constr.params.length; j++) { if (!mask[j]) { this.params.push(constr.params[j]); } @@ -157,7 +158,7 @@ function ConstantWrapper(constr, mask) { fillArray(this.grad, 0, this.grad.length, 0); constr.gradient(this.grad); var jj = 0; - for (j = 0; j < mask.length; j++) { + for (let j = 0; j < mask.length; j++) { if (!mask[j]) { out[jj ++] = this.grad[j]; } @@ -212,6 +213,29 @@ function Diff(params, value) { }; } +function P2LDistanceSigned(params, value) { + + const TX = 0; + const TY = 1; + const AX = 2; + const AY = 3; + const BX = 4; + const BY = 5; + + this.params = params; + this.value = value; + + this.error = function() { + const tx = params[TX].get(), ax = params[AX].get(), bx = params[BX].get(); + const ty = params[TY].get(), ay = params[AY].get(), by = params[BY].get(); + const d = Math.sqrt(sq(by - ay) + sq(bx - ax)); + + return (-(by - ay) * (tx - ax) ) / d + ((bx - ax) * (ty - ay)) / d - this.value; + }; + + this.gradient = NumericGradient; +} + function P2LDistance(params, distance) { this.params = params; diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index a242c935..53c4b7de 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -13,9 +13,11 @@ function Param(id, value, readOnly) { Param.prototype.reset = function(value) { this.set(value); this.j = -1; + this.aux = false; }; Param.prototype.set = function(value) { + if (this.aux) return; this.value = value; }; @@ -187,28 +189,27 @@ System.prototype.getValues = function() { return values; }; -var wrapAux = function(constrs, locked) { - - var i, lockedSet = {}; - for (i = 0; i < locked.length; i++) { - lockedSet[locked[i].j] = true; - } - - for (i = 0; i < constrs.length; i++) { - var c = constrs[i]; - var mask = []; - var needWrap = false; - for (var j = 0; j < c.params.length; j++) { - var param = c.params[j]; - mask[j] = lockedSet[param.j] === true; +function wrapAux(constrs) { + for (let i = 0; i < constrs.length; i++) { + const c = constrs[i]; + const mask = []; + let needWrap = false; + for (let j = 0; j < c.params.length; j++) { + const param = c.params[j]; + mask[j] = param.aux === true; needWrap = needWrap || mask[j]; } if (needWrap) { - var wrapper = new ConstantWrapper(c, mask); - constrs[i] = wrapper; + constrs[i] = new ConstantWrapper(c, mask); } } -}; + for (let constr of constrs) { + if (constr.params.length == 0) { + return constrs.filter(c => c.params.length != 0); + } + } + return constrs; +} var lock2Equals2 = function(constrs, locked) { var _locked = []; @@ -233,18 +234,17 @@ var diagnose = function(sys) { } }; -var prepare = function(constrs, locked, aux, alg) { +var prepare = function(constrs, locked) { var simpleMode = true; if (!simpleMode) { var lockingConstrs = lock2Equals2(constrs, locked); Array.prototype.push.apply( constrs, lockingConstrs ); } - + + constrs = wrapAux(constrs); var sys = new System(constrs); - wrapAux(constrs, aux); - var model = function(point) { sys.setParams(point); return sys.getValues(); diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 6efe5ea1..646b7570 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -485,29 +485,23 @@ ParametricManager.prototype.__getSolveData = function(constraints, out) { return out; }; -ParametricManager.prototype.solve = function() { - var solver = this.prepare([]); +ParametricManager.prototype.solve = function(lock, extraConstraints, disabledObjects) { + const solver = this.prepare(lock, extraConstraints, disabledObjects); solver.solve(false); solver.sync(); }; -ParametricManager.prototype.solveWithLock = function(lock) { - var solver = this.prepare(lock); - solver.solve(false); - solver.sync(); +ParametricManager.prototype.prepare = function(locked, extraConstraints, disabledObjects) { + return this._prepare(locked, this.subSystems, extraConstraints, disabledObjects); }; -ParametricManager.prototype.prepare = function(locked, extraConstraints) { - return this._prepare(locked, this.subSystems, extraConstraints); -}; - -ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints) { +ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints, disabledObjects) { var solvers = []; for (var i = 0; i < subSystems.length; i++) { - solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints)); + solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints, disabledObjects)); } if (subSystems.length == 0 && locked.length != 0) { - solvers.push(this.prepareForSubSystem(locked, [], extraConstraints)); + solvers.push(this.prepareForSubSystem(locked, [], extraConstraints, disabledObjects)); } return { solvers : solvers, @@ -545,9 +539,9 @@ ParametricManager.prototype._prepare = function(locked, subSystems, extraConstra } }; -ParametricManager.isAux = function(obj) { +ParametricManager.isAux = function(obj, disabledObjects) { while (!!obj) { - if (!!obj.aux) { + if (!!obj.aux || (disabledObjects !== undefined && disabledObjects.has(obj))) { return true; } obj = obj.parent; @@ -555,12 +549,13 @@ ParametricManager.isAux = function(obj) { return false; }; -ParametricManager.fetchAuxParams = function(system, auxParams, auxDict) { +ParametricManager.fetchAuxParams = function(system, auxParams, auxDict, disabledObjects) { + disabledObjects = disabledObjects != undefined ? new Set(disabledObjects) : undefined; for (var i = 0; i < system.length; ++i) { for (var p = 0; p < system[i][1].length; ++p) { var parameter = system[i][1][p]; if (parameter.obj !== undefined) { - if (ParametricManager.isAux(parameter.obj)) { + if (ParametricManager.isAux(parameter.obj, disabledObjects)) { if (auxDict[parameter.id] === undefined) { auxDict[parameter.id] = parameter; auxParams.push(parameter); @@ -726,7 +721,9 @@ ParametricManager.reduceSystem = function(system, readOnlyParams) { return info; }; -ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemConstraints, extraConstraints) { +ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemConstraints, extraConstraints, disabledObjects) { + + locked = locked || []; var constrs = []; var solverParamsDict = {}; @@ -737,7 +734,7 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons this.__getSolveData(subSystemConstraints, system); if (!!extraConstraints) this.__getSolveData(extraConstraints, system); - ParametricManager.fetchAuxParams(system, auxParams, auxDict); + ParametricManager.fetchAuxParams(system, auxParams, auxDict, disabledObjects); var readOnlyParams = auxParams.concat(locked); var reduceInfo = ParametricManager.reduceSystem(system, readOnlyParams); @@ -777,19 +774,16 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons } })(); - var aux = []; for (var i = 0; i < system.length; ++i) { var sdata = system[i]; var params = []; - for (var p = 0; p < sdata[1].length; ++p) { - var param = sdata[1][p]; - var solverParam = getSolverParam(param); + for (let p = 0; p < sdata[1].length; ++p) { + const param = sdata[1][p]; + const solverParam = getSolverParam(param); + solverParam.aux = auxDict[param.id] !== undefined; params.push(solverParam); - if (auxDict[param.id] !== undefined) { - aux.push(solverParam); - } } if (reduceInfo.reducedConstraints[i] === true) continue; @@ -798,11 +792,11 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons } var lockedSolverParams = []; - for (p = 0; p < locked.length; ++p) { + for (let p = 0; p < locked.length; ++p) { lockedSolverParams[p] = getSolverParam(locked[p]); } - var solver = prepare(constrs, lockedSolverParams, aux); + var solver = prepare(constrs, lockedSolverParams); function solve(rough, alg) { return solver.solveSystem(rough, alg); } @@ -1015,6 +1009,45 @@ Constraints.Perpendicular.prototype.getObjects = function() { // ------------------------------------------------------------------------------------------------------------------ // +/** @constructor */ +Constraints.P2LDistanceSigned = function(p, a, b, d) { + this.p = p; + this.a = a; + this.b = b; + this.d = d; +}; + +Constraints.P2LDistanceSigned.prototype.NAME = 'P2LDistanceSigned'; +Constraints.P2LDistanceSigned.prototype.UI_NAME = 'Distance P & L'; + +Constraints.P2LDistanceSigned.prototype.getSolveData = function(resolver) { + var params = []; + this.p.collectParams(params); + this.a.collectParams(params); + this.b.collectParams(params); + return [[this.NAME, params, [resolver(this.d)]]]; +}; + +Constraints.P2LDistanceSigned.prototype.serialize = function() { + return [this.NAME, [this.p.id, this.a.id, this.b.id, this.d]]; +}; + +Constraints.Factory[Constraints.P2LDistanceSigned.prototype.NAME] = function(refs, data) { + return new Constraints.P2LDistanceSigned(refs(data[0]), refs(data[1]), refs(data[2]), data[3]); +}; + +Constraints.P2LDistanceSigned.prototype.getObjects = function() { + const collector = new Constraints.ParentsCollector(); + collector.check(this.a); + collector.check(this.b); + collector.parents.push(this.p); + return collector.parents; +}; + +Constraints.P2LDistanceSigned.prototype.SettableFields = {'d' : "Enter the distance"}; + +// ------------------------------------------------------------------------------------------------------------------ // + /** @constructor */ Constraints.P2LDistance = function(p, l, d) { this.p = p; diff --git a/web/app/sketcher/tools/fillet.js b/web/app/sketcher/tools/fillet.js index b46d8027..459f49d3 100644 --- a/web/app/sketcher/tools/fillet.js +++ b/web/app/sketcher/tools/fillet.js @@ -83,8 +83,8 @@ export class FilletTool extends Tool { //pm._add(new Constraints.LockConvex(arc.c, arc.a, otherEnd(point1))); //pm._add(new Constraints.LockConvex(otherEnd(point2), arc.b, arc.c)); - var solver = pm.solveWithLock([]); - // var solver = pm.solveWithLock([point1._x, point1._y, point2._x, point2._y]); + var solver = pm.solve(); + // var solver = pm.solve([point1._x, point1._y, point2._x, point2._y]); pm.notify(); this.viewer.refresh(); } diff --git a/web/app/sketcher/tools/offset.js b/web/app/sketcher/tools/offset.js index bbbfdf9a..db2d9c47 100644 --- a/web/app/sketcher/tools/offset.js +++ b/web/app/sketcher/tools/offset.js @@ -25,8 +25,11 @@ export class OffsetTool extends LoopPickTool { } } let delta = parseInt(prompt('offset distance?', 100)); + if (isNaN(delta)) { + return; + } const absDelta = Math.abs(delta); - + const edges = []; const startPoint = findLowestPoint(loopPoints); const start = loopPoints.indexOf(startPoint); @@ -38,15 +41,14 @@ export class OffsetTool extends LoopPickTool { return (i + start) % length; } - if (!math.isCCW([loopPoints[pos(0)], loopPoints[pos(1)], loopPoints[pos(length - 1)]])) { + const inverse = this.twoConnectedArcs() || !math.isCCW([loopPoints[pos(0)], loopPoints[pos(1)], loopPoints[pos(length - 1)]]); + if (inverse) { delta *= -1; } - const arcs = []; - for (let i = 0; i < length; ++i) { - const a = loopPoints[pos(i)]; - const b = loopPoints[pos(i + 1)]; + let a = loopPoints[pos(i)]; + let b = loopPoints[pos(i + 1)]; const normal = new Vector(-(b.y - a.y), (b.x - a.x))._normalize(); const offVector = normal._multiply(delta); const origEdge = loopEdges[pos(i)]; @@ -58,33 +60,39 @@ export class OffsetTool extends LoopPickTool { const segment = this.viewer.addSegment(aOffX, aOffY, bOffX, bOffY, this.viewer.activeLayer); this.viewer.parametricManager._add(new Constraints.Parallel(origEdge, segment)); - this.viewer.parametricManager._add(new Constraints.P2LDistance(a, segment, absDelta)); + this.viewer.parametricManager._add(new Constraints.P2LDistanceSigned(a, segment.b, segment.a, delta)); edges.push(segment); } else if (origEdge._class == 'TCAD.TWO.Arc') { + const connectionEdge = new SimpleEdge(new EndPoint(aOffX, aOffY), new EndPoint(bOffX, bOffY)); + edges.push(connectionEdge); + const arcEdge = inverse ? connectionEdge.reverse() : connectionEdge; const arc = new Arc( - new EndPoint(aOffX, aOffY), - new EndPoint(bOffX, bOffY), + arcEdge.a, + arcEdge.b, new EndPoint(origEdge.c.x + offVector.x, origEdge.c.y + offVector.y) ); arc.stabilize(this.viewer); - this.viewer.parametricManager._linkObjects([origEdge.c, arc.c]); - this.viewer.parametricManager._add(new Constraints.RadiusOffset(origEdge, arc, delta)); + this.viewer.parametricManager._linkObjects([arc.c, origEdge.c]); + this.viewer.parametricManager._add(new Constraints.RadiusOffset(inverse?arc:origEdge, inverse?origEdge:arc, delta)); this.viewer.activeLayer.add(arc); - edges.push(arc); - arcs.push(arc); } } - arcs.forEach(e => e.c.aux = true); for (let i = 0; i < edges.length; i++) { this.viewer.parametricManager._linkObjects([edges[i].b, edges[(i + 1) % edges.length].a]); } - loopEdges.forEach(e => e.aux = true); + this.viewer.parametricManager.solve(undefined, undefined, loopEdges); this.viewer.parametricManager.refresh(); - loopEdges.forEach(e => e.aux = false); - arcs.forEach(e => e.c.aux = false); this.viewer.toolManager.releaseControl(); } + + twoConnectedArcs() { + function isArc(edge) { + return edge._class == 'TCAD.TWO.Arc'; + } + const edges = this.pickedLoop.edges; + return edges.length == 2 && isArc(edges[0]) && isArc(edges[1]); + } } @@ -96,9 +104,12 @@ const SUPPORTED_OBJECTS = new Set(); SUPPORTED_OBJECTS.add('TCAD.TWO.Segment'); SUPPORTED_OBJECTS.add('TCAD.TWO.Arc'); -function SimpleSegment(a, b) { +function SimpleEdge(a, b) { this.a = a; this.b = b; + this.reverse = function() { + return new SimpleEdge(b, a); + } } function findLowestPoint(poly) {