diff --git a/modules/gems/func.js b/modules/gems/func.js index e0e91c7c..c5bffbba 100644 --- a/modules/gems/func.js +++ b/modules/gems/func.js @@ -34,5 +34,6 @@ export function compositeFn() { funcs.push(fn); } } + fn.push = push; return fn } diff --git a/web/app/math/optim.js b/web/app/math/optim.js index ca7c4bec..c87e337c 100644 --- a/web/app/math/optim.js +++ b/web/app/math/optim.js @@ -1,6 +1,9 @@ import numeric from 'numeric'; import {_vec, _matrix} from './math' +const SUCCESS = 1, ITER_LIMIT = 2, SMALL_DELTA = 3, SMALL_STEP = 4, DIVERGENCE = 5, INVALID_STATE = 6; + + //Added strong wolfe condition to numeric's uncmin export function fmin_bfgs(f,x0,tol,gradient,maxit,callback,options) { var grad = numeric.gradient; @@ -256,9 +259,10 @@ var inv = function inv(A) { }; var _result = function(evalCount, error, returnCode) { - this.evalCount = evalCount; - this.error = error; - this.returnCode = returnCode; + return { + evalCount, error, returnCode, + success: returnCode === SUCCESS + }; }; var dog_leg = function (subsys, rough) { @@ -279,7 +283,7 @@ var dog_leg = function (subsys, rough) { var csize = subsys.constraints.length; if (xsize == 0) { - return new _result(0, 0, 1); + return _result(0, 0, 1); } var vec = _vec; @@ -330,8 +334,6 @@ var dog_leg = function (subsys, rough) { var iter = 0, returnCode = 0; //var log = []; - var SUCCESS = 1, ITER_LIMIT = 2, SMALL_DELTA = 3, SMALL_STEP = 4, DIVERGENCE = 5, INVALID_STATE = 6; - while (returnCode === 0) { optim.DEBUG_HANDLER(iter, err); @@ -457,7 +459,7 @@ var dog_leg = function (subsys, rough) { } //log.push(returnCode); //window.___log(log); - return new _result(iter, err, returnCode); + return _result(iter, err, returnCode); }; var cg = function(A, x, b, tol, maxIt) { diff --git a/web/app/sketcher/actions/constraintActions.js b/web/app/sketcher/actions/constraintActions.js index 99761305..1c815a7c 100644 --- a/web/app/sketcher/actions/constraintActions.js +++ b/web/app/sketcher/actions/constraintActions.js @@ -105,6 +105,46 @@ export default [ pm.refresh(); }); } + }, + + { + shortName: 'Length', + description: 'Segment Length', + selectionMatcher: (selection) => matchAll(selection, Segment, 1), + + invoke: ctx => { + const {viewer} = ctx; + + const [firstSegment, ...others] = viewer.selected; + + const firstConstr = new AlgNumConstraint(ConstraintDefinitions.SegmentLength, [firstSegment]); + firstConstr.initConstants(); + + editConstraint(ctx, firstConstr, () => { + const pm = viewer.parametricManager; + pm.algnNumSystem.addConstraint(firstConstr); + for (let other of others) { + pm.algnNumSystem.addConstraint(new AlgNumConstraint(ConstraintDefinitions.SegmentLength, [other], {...firstConstr.constants})); + } + pm.refresh(); + }); + } + }, + + { + shortName: 'Lock', + description: 'Lock Point', + selectionMatcher: (selection) => matchTypes(selection, EndPoint, 1), + + invoke: ctx => { + const {viewer} = ctx; + + const [point] = viewer.selected; + + const constr = new AlgNumConstraint(ConstraintDefinitions.LockPoint, [point]); + constr.initConstants(); + editConstraint(ctx, constr, () => viewer.parametricManager.addAlgNum(constr)); + } } ]; diff --git a/web/app/sketcher/constr/ANConstraints.js b/web/app/sketcher/constr/ANConstraints.js index d28a207a..3aaf9df1 100644 --- a/web/app/sketcher/constr/ANConstraints.js +++ b/web/app/sketcher/constr/ANConstraints.js @@ -111,18 +111,13 @@ export const ConstraintDefinitions = indexById([ initialValue: (constraint) => { const [a, b] = constraint.objects; return distanceAB(a, b); - } + }, } }, - defineParamsScope: ([pt, segment], callback) => { - pt.visitParams(callback); - callback(segment.params.ang); - callback(segment.params.w); - }, - - collectResiduals: (residuals, params, {distance}) => { - residuals.push([R_DistancePP, params, [distance]]); + defineParamsScope: ([pt1, pt2], callback) => { + pt1.visitParams(callback); + pt2.visitParams(callback); }, collectPolynomials: (polynomials, [x1, y1, x2, y2], {distance}) => { @@ -201,6 +196,85 @@ export const ConstraintDefinitions = indexById([ }, }, + { + id: 'SegmentLength', + name: 'Segment Length', + constants: { + length: { + type: 'number', + description: 'length of the segment', + initialValue: (constraint) => { + const [segment] = constraint.objects; + const dx = segment.b.x - segment.a.x; + const dy = segment.b.y - segment.a.y; + return Math.sqrt(dx*dx + dy*dy); + }, + + // transform: length => length * length + } + }, + + defineParamsScope: ([segment], callback) => { + callback(segment.params.t); + }, + + collectPolynomials: (polynomials, [t], {length}) => { + polynomials.push(new Polynomial( - length).monomial(1).term(t, POW_1_FN)); + }, + }, + + { + id: 'Polar', + name: 'Polar Coordinate', + + defineParamsScope: ([segment, originPt, targetPt], callback) => { + callback(segment.params.ang); + callback(segment.params.t); + originPt.visitParams(callback); + targetPt.visitParams(callback); + }, + + collectPolynomials: (polynomials, [ang, t, x1, y1, x2, y2]) => { + + + // v = [sin(ang), - cos(ang)] + // v * t = pt2 - pt1 + + //sin(ang) * t - x2 + x1 + //-cos(ang) * t - y2 + y1 + + polynomials.push(new Polynomial().monomial() .term(ang, SIN_FN).term(t, POW_1_FN).monomial(-1).term(x2, POW_1_FN).monomial(1).term(x1, POW_1_FN)); + polynomials.push(new Polynomial().monomial(-1).term(ang, COS_FN).term(t, POW_1_FN).monomial(-1).term(y2, POW_1_FN).monomial(1).term(y1, POW_1_FN)); + }, + }, + + + { + id: 'LockPoint', + name: 'Lock Point', + constants: { + x: { + type: 'number', + description: 'X Coordinate', + initialValue: (constraint) => constraint.objects[0].x, + }, + y: { + type: 'number', + description: 'y Coordinate', + initialValue: (constraint) => constraint.objects[0].y, + } + }, + + defineParamsScope: ([pt], callback) => { + pt.visitParams(callback); + }, + + collectPolynomials: (polynomials, [px, py], {x, y}) => { + polynomials.push(new Polynomial(-x).monomial().term(px, POW_1_FN)); + polynomials.push(new Polynomial(-y).monomial().term(py, POW_1_FN)); + }, + } + ]); export class AlgNumConstraint { diff --git a/web/app/sketcher/constr/AlgNumSystem.js b/web/app/sketcher/constr/AlgNumSystem.js index 50b62576..0c97e759 100644 --- a/web/app/sketcher/constr/AlgNumSystem.js +++ b/web/app/sketcher/constr/AlgNumSystem.js @@ -11,7 +11,7 @@ export class AlgNumSubSystem { generator = NOOP;//new BoundaryObjectsGenerator(); - constraints = []; + allConstraints = []; ownParams = new Set(); @@ -21,6 +21,7 @@ export class AlgNumSubSystem { beingSolvedParams = new Set(); + eliminatedParams = new Map(); // generators = []; subSystems = []; //subsystems are generated by generators only @@ -28,13 +29,31 @@ export class AlgNumSubSystem { polynomials = []; substitutedParams = []; polyToConstr = new Map(); + conflicting = new Set(); redundant = new Set(); + snapshot = new Map(); + + dof = 0; + SubSystem(generator) { this.generator = generator; } + + get fullyConstrained() { + return this.dof === 0; + } + + validConstraints(callback) { + this.allConstraints.forEach(c => { + if (!this.conflicting.has(c)) { + callback(c); + } + }); + } + addConstraint(constraint, _ancestorParams) { if (this.canBeAdded(constraint.params)) { @@ -42,20 +61,73 @@ export class AlgNumSubSystem { // this.constraints } - this.constraints.push(constraint); + this.makeSnapshot(); + + this.allConstraints.push(constraint); + + this.prepare(); + if (!this.isConflicting(constraint)) { + this.solveFine(); + console.log(this.solveStatus); + if (!this.solveStatus.success) { + console.log("adding to conflicts"); + this.conflicting.add(constraint); + } + } + + if (this.isConflicting(constraint)) { + this.rollback(); + // } else if (this.fullyConstrained) { + // this.rollback(); + // this.conflicting.add(constraint); + // this.redundant.add(constraint); + } else { + this.updateDOF(); + this.updateFullyConstrainedObjects(); + } + + } + + isConflicting(constraint) { + return this.conflicting.has(constraint); + } + + updateDOF() { + + let vars = 0; + let equations = 0; + + this.validConstraints(c => { + equations ++; + c.params.forEach(p => { + if (!this.readOnlyParams.has(p)) { + vars++ + } + }) + }); + + this.dof = vars - equations; + } + + makeSnapshot() { + this.snapshot.clear(); + this.validConstraints(c => c.params.forEach(p => this.snapshot.set(p, p.get()))); + } + + rollback() { + this.snapshot.forEach((val, param) => param.set(val)); } reset() { this.polynomials = []; this.substitutedParams = []; - this.polyToConstr.clear(); - this.conflicting.clear(); - this.redundant.clear(); + this.eliminatedParams.clear(); this.beingSolvedParams.clear(); + this.polyToConstr.clear(); } evaluatePolynomials() { - this.constraints.forEach(c => { + this.validConstraints(c => { c.collectPolynomials(this.polynomials); this.polynomials.forEach(p => this.polyToConstr.set(p, c)) }); @@ -63,11 +135,6 @@ export class AlgNumSubSystem { console.log('reducing system:'); this.polynomials.forEach(p => console.log(p.toString())); - const toEliminate = Array.from(this.readOnlyParams); - - const toSubstitute = []; - const linearSub = []; - let requirePass = true; while (requirePass) { @@ -79,74 +146,74 @@ export class AlgNumSubSystem { } if (polynomial.monomials.length === 0) { - if (!eqEps(polynomial.constant, 0)) { - this.conflicting.add(this.polyToConstr.get(polynomial)); + this.conflicting.add(this.polyToConstr.get(polynomial)); + console.log("CONFLICT: " + polynomial.toString()); + if (eqEps(polynomial.constant, 0)) { + this.redundant.add(this.polyToConstr.get(polynomial)); + console.log("REDUNDANT"); } this.polynomials[i] = null; - } else if (polynomial.monomials.length === 1) { + } else if (polynomial.isLinear && polynomial.monomials.length === 1) { const monomial = polynomial.monomials[0]; const terms = monomial.terms; if (terms.length === 1) { const term = terms[0]; if (term.fn.degree === 1) { - term.param.set(- polynomial.constant / monomial.constant); - toEliminate.push(term.param); + const p = term.param; + const val = - polynomial.constant / monomial.constant; + p.set(val); + + this.eliminatedParams.set(p, val); + + for (let polynomial of this.polynomials) { + if (polynomial) { + polynomial.eliminate(p, val); + } + } + + requirePass = true; } } this.polynomials[i] = null; } else if (polynomial.monomials.length === 2 && polynomial.isLinear) { const [m1, m2] = polynomial.monomials; + let p1 = m1.linearParam; + let p2 = m2.linearParam; const constant = - m2.constant / m1.constant; if (eqEps(polynomial.constant, 0)) { - toSubstitute.push([m1.linearParam, m2.linearParam, constant]); - this.substitutedParams.push([m1.linearParam, new Polynomial().monomial(constant).term(m2.linearParam, POW_1_FN)]); - } else { - linearSub.push([m1.param, m2.param, constant, - polynomial.constant / m1.constant]); - } - this.polynomials[i] = null; - } - } - while (toEliminate.length) { - requirePass = true; - const param = toEliminate.pop(); - for (let polynomial of this.polynomials) { - if (polynomial) { - polynomial.eliminate(param, param.get()); - } - } - } - while (toSubstitute.length) { - requirePass = true; - const [param, toParam, dotConstant] = toSubstitute.pop(); - for (let polynomial of this.polynomials) { - if (polynomial) { - polynomial.substitute(param, toParam, dotConstant); - } - } - } - - if (linearSub.length) { - while (linearSub.length) { - const [param, toParam, k, b] = linearSub.pop(); - - let transaction = compositeFn(); - for (let polynomial of this.polynomials) { - if (polynomial) { - const polyTransaction = polynomial.linearSubstitution(param, toParam, k, b); - if (!polyTransaction) { - transaction = null; - break; + for (let polynomial of this.polynomials) { + if (polynomial) { + polynomial.substitute(p1, p2, constant); } - transaction.push(polyTransaction); - transaction.push(() => this.substitutedParams.push(param, new Polynomial(b).monomial(k).term(toParam, POW_1_FN))); } - } - if (transaction) { + this.substitutedParams.push([p1, new Polynomial().monomial(constant).term(p2, POW_1_FN)]); + this.polynomials[i] = null; requirePass = true; - transaction(); + } else { + const b = - polynomial.constant / m1.constant; + + let transaction = compositeFn(); + for (let polynomial of this.polynomials) { + if (polynomial) { + const polyTransaction = polynomial.linearSubstitution(p1, p2, constant, b); + if (!polyTransaction) { + transaction = null; + break; + } + transaction.push(polyTransaction); + transaction.push(() => { + this.substitutedParams.push([p1, new Polynomial(b).monomial(constant).term(p2, POW_1_FN)]); + this.polynomials[i] = null; + }); + } + } + if (transaction) { + transaction(); + requirePass = true; + } } } } @@ -168,12 +235,13 @@ export class AlgNumSubSystem { this.evaluatePolynomials(); + // this.polynomialClusters = splitByIsolatedClusters(this.polynomials); + console.log('solving system:'); this.polynomials.forEach(p => console.log(p.toString())); console.log('with respect to:'); this.substitutedParams.forEach(([x, expr]) => console.log('X' + x.id + ' = ' + expr.toString())); - const residuals = []; this.polynomials.forEach(p => residuals.push(p.asResidual())); @@ -189,6 +257,63 @@ export class AlgNumSubSystem { this.numericalSolver = prepare(residuals); } + splitByIsolatedClusters(polynomials) { + + const graph = [polynomials.length]; + const params = []; + const paramMap = new Map(); + polynomials.forEach((pl, i) => pl.visitParams(p => { + let pIndex = paramMap.get(p); + if (pIndex === undefined) { + pIndex = paramMap.size; + paramMap.set(p, pIndex); + params.push(p); + } + graph[i][pIndex] = 1; + })); + + const visited = new Set(); + const clusters = []; + + for (let param of params) { + if (visited.has(param)) { + continue + } + const stack = [param]; + const isolation = []; + while (stack.length) { + const p = stack.pop(); + if (visited.has(p)) { + continue; + } + isolation.push(p); + const index = paramMap.get(p); + for (let i = 0; i < graph.length; ++i) { + if (graph[i][index] === 1) { + for (let j = 0; j < params.length; j ++) { + if (j !== index) { + stack.push(params[j]); + } + } + } + } + } + if (isolation.length) { + clusters.push(new Cluster(isolation)); + } + } + + return clusters; + } + + solveRough() { + this.solve(true); + } + + solveFine() { + this.solve(false); + } + solve(rough) { this.generator(); @@ -197,7 +322,15 @@ export class AlgNumSubSystem { solverParam.set(solverParam.objectParam.get()); }); - this.numericalSolver.solveSystem(rough); + const solveStatus = this.numericalSolver.solveSystem(rough); + if (!rough) { + this.solveStatus = solveStatus; + } + console.log('numerical result: ' + solveStatus.success); + + for (let [p, val] of this.eliminatedParams) { + p.set(val); + } this.beingSolvedParams.forEach(solverParam => { solverParam.objectParam.set(solverParam.get()); @@ -208,6 +341,27 @@ export class AlgNumSubSystem { } } + updateFullyConstrainedObjects() { + + + + this.validConstraints(c => { + + c.objects.forEach(obj => { + + let allLocked = true; + + obj.visitParams(p => { + + if (!this.eliminatedParams.has(p)) { + allLocked = false; + } + }); + + obj.fullyConstrained = allLocked; + }); + }); + } canBeAdded(subjectParams, ancestorParams) { @@ -223,6 +377,41 @@ export class AlgNumSubSystem { } +class Cluster { + + constructor(polynomials) { + this.polynomials = polynomials; + this.beingSolvedParams = new Set(); + const residuals = []; + + this.polynomials.forEach(p => residuals.push(p.asResidual())); + + for (let residual of residuals) { + residual.params.forEach(solverParam => { + if (!this.beingSolvedParams.has(solverParam)) { + solverParam.reset(solverParam.objectParam.get()); + this.beingSolvedParams.add(solverParam); + } + }); + } + + } + + solve(rough) { + this.beingSolvedParams.forEach(solverParam => { + solverParam.set(solverParam.objectParam.get()); + }); + + this.solveStatus = this.numericalSolver.solveSystem(rough); + + this.beingSolvedParams.forEach(solverParam => { + solverParam.objectParam.set(solverParam.get()); + }); + } + +} + + export class AlgNumSystem { constraints = []; generators = []; diff --git a/web/app/sketcher/constr/polynomial.js b/web/app/sketcher/constr/polynomial.js index da39fac8..35a85d84 100644 --- a/web/app/sketcher/constr/polynomial.js +++ b/web/app/sketcher/constr/polynomial.js @@ -38,11 +38,16 @@ export class Polynomial { substitute(param, toParam, dotConstant) { for (let m of this.monomials) { + let touched = false; for (let i = 0; i < m.terms.length; ++i) { if (m.terms[i].param === param) { m.substitute(i, toParam, dotConstant); + touched = true; } } + if (touched) { + m.mergeTerms(); + } } } @@ -161,6 +166,14 @@ export class Polynomial { } + visitParams(callback) { + for (let m of this.monomials) { + for (let t of m.terms) { + callback(t.param); + } + } + } + toString() { return this.monomials.map(m => { @@ -227,8 +240,12 @@ export class Monomial { this.terms.splice(i, 1); } - substitute(i, toParam, dotConstant) { + substitute(index, toParam, dotConstant) { + this.terms[index].param = toParam; this.constant *= dotConstant; + } + + mergeTerms() { let wasMerge = false; for (let i = 0; i < this.terms.length; ++i) { const merger = this.terms[i]; @@ -250,7 +267,6 @@ export class Monomial { if (wasMerge) { this.terms = this.terms.filter(t => t); } - } equalVars(other) { @@ -321,6 +337,7 @@ export class ToThePowerFunction { } constructor(degree, fn, d1) { + this.degree = degree; this.fn = fn; this.d1 = d1; this.id = '^' + degree; @@ -358,10 +375,11 @@ export class FreeFunction { fn; - constructor(fn, d1, id) { + constructor(fn, d1, id, linearSubstitutionFn) { this.fn = fn; this.d1 = d1; this.id = id; + this.linearSubstitutionFn = linearSubstitutionFn || null; } apply(x) { @@ -381,7 +399,7 @@ export class FreeFunction { } linearSubstitution(k, x, b) { - return null; + return this.linearSubstitutionFn(k, x, b); } } @@ -415,7 +433,7 @@ export class CosineOfSum { } render(x) { - return 'cos(' + this.k.toFixed(2) + x + ' + ' + this.b.toFixed(2) + ')'; + return 'cos(' + this.k.toFixed(2) + '*' + x + ' + ' + this.b.toFixed(2) + ')'; } } @@ -449,7 +467,7 @@ export class SineOfSum { } render(x) { - return 'sin(' + this.k.toFixed(2) + x + ' + ' + this.b.toFixed(2) + ')'; + return 'sin(' + this.k.toFixed(2) + '*' + x + ' + ' + this.b.toFixed(2) + ')'; } } diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index 8a854c70..537705e3 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -400,7 +400,7 @@ IO.prototype._serializeSketch = function(metadata) { } sketch.constraints = []; - const systemConstraints = this.viewer.parametricManager.algnNumSystem.constraints; + const systemConstraints = this.viewer.parametricManager.algnNumSystem.allConstraints; for (let sc of systemConstraints) { if (!sc.internal) { sketch.constraints.push(sc.write()); diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 687fa030..92ced3c4 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -105,6 +105,10 @@ class ParametricManager { this.refresh(); } + coincidePoints(pt1, pt2) { + this.addAlgNum(new AlgNumConstraint(ConstraintDefinitions.PCoincident, [pt1, pt2])); + } + } ParametricManager.prototype.createConstantResolver = function() { diff --git a/web/app/sketcher/shapes/segment.js b/web/app/sketcher/shapes/segment.js index e4c7285f..1390a816 100644 --- a/web/app/sketcher/shapes/segment.js +++ b/web/app/sketcher/shapes/segment.js @@ -47,18 +47,15 @@ export class Segment extends SketchObject { let ang = Math.atan2(ny, nx); - if (this.ang !== undefined && Math.abs(ang - this.ang) > Math.PI) { - ang = Math.atan2(-ny, -nx); - } - this.params.ang.set(ang||0); this.params.w.set(nx * this.a.x + ny * this.a.y); + this.params.t.set(l); } stabilize(viewer) { this.syncGeometry(); const c1 = new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [this.a, this]); - const c2 = new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [this.b, this]); + const c2 = new AlgNumConstraint(ConstraintDefinitions.Polar, [this, this.a, this.b]); c1.internal = true; c2.internal = true; viewer.parametricManager.addAlgNum(c1); diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index b1596fc4..52bfe303 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -1,6 +1,7 @@ import {Generator} from '../id-generator' import {Shape} from './shape' import {Types} from '../io'; +import {Styles} from "../styles"; export class SketchObject extends Shape { constructor() { @@ -11,6 +12,7 @@ export class SketchObject extends Shape { this.children = []; this.linked = []; this.layer = null; + this.fullyConstrained = false; } normalDistance(aim, scale) { @@ -86,8 +88,12 @@ export class SketchObject extends Shape { ctx.save(); viewer.setStyle(this.marked, ctx); } + if (this.fullyConstrained) { + ctx.save(); + viewer.setStyle(Styles.FULLY_CONSTRAINED, ctx); + } this.drawImpl(ctx, scale, viewer); - if (this.marked != null) ctx.restore(); + if (this.marked != null || this.fullyConstrained) ctx.restore(); } copy() { diff --git a/web/app/sketcher/styles.js b/web/app/sketcher/styles.js index 8542ea9e..0f3ccf00 100644 --- a/web/app/sketcher/styles.js +++ b/web/app/sketcher/styles.js @@ -39,6 +39,12 @@ export const Styles = { fillStyle : "#000000" }, + FULLY_CONSTRAINED: { + lineWidth : 2, + strokeStyle : "#d7e6ff", + fillStyle : "#424e56" + }, + CONSTRUCTION : { lineWidth : 1, strokeStyle : "#aaaaaa", diff --git a/web/app/sketcher/tools/drag.js b/web/app/sketcher/tools/drag.js index c8cef0b4..2d9b253b 100644 --- a/web/app/sketcher/tools/drag.js +++ b/web/app/sketcher/tools/drag.js @@ -23,7 +23,7 @@ export class DragTool extends Tool { // this.lockedValues[i + 1] = this._point.y - this.lockedShifts[i + 1]; // } this.obj.translate(dx, dy); - + (this.obj.parent || this.obj).syncGeometry(); if (!Tool.dumbMode(e)) { this.viewer.parametricManager.algnNumSystem.solve(true); } diff --git a/web/app/sketcher/tools/segment.js b/web/app/sketcher/tools/segment.js index ca55ca77..cb196281 100644 --- a/web/app/sketcher/tools/segment.js +++ b/web/app/sketcher/tools/segment.js @@ -19,7 +19,7 @@ export class AddSegmentTool extends Tool { } mousemove(e) { - var p = this.viewer.screenToModel(e); + let p = this.viewer.screenToModel(e); if (this.line != null) { this.viewer.snap(p.x, p.y, [this.line.a, this.line.b]); this.line.b.x = p.x; @@ -35,7 +35,7 @@ export class AddSegmentTool extends Tool { if (this.line == null) { const b = this.viewer.screenToModel(e); let a = b; - var needSnap = false; + let needSnap = false; if (this.viewer.snapped != null) { a = this.viewer.snapped; this.viewer.cleanSnap(); @@ -43,7 +43,7 @@ export class AddSegmentTool extends Tool { } this.line = this.viewer.addSegment(a.x, a.y, b.x, b.y, this.viewer.activeLayer); if (needSnap) { - this.line.a.coincideWith(a); + this.viewer.parametricManager.coincidePoints(this.line.a, a); } this.firstPointPicked(); this.viewer.refresh(); @@ -53,7 +53,7 @@ export class AddSegmentTool extends Tool { this.viewer.cleanSnap(); this.line.b.x = p.x; this.line.b.y = p.y; - this.line.b.coincideWith(p); + this.viewer.parametricManager.coincidePoints(this.line.b, p); } this.nextPointPicked(); } @@ -61,10 +61,11 @@ export class AddSegmentTool extends Tool { nextPointPicked() { this.pointPicked(this.line.b.x, this.line.b.y); + this.line.stabilize(this.viewer); if (this.multi) { const b = this.line.b; this.line = this.viewer.addSegment(b.x, b.y, b.x, b.y, this.viewer.activeLayer); - this.line.a.coincideWith(b); + this.viewer.parametricManager.coincidePoints(this.line.a, b); } else { this.restart() } diff --git a/web/app/sketcher/viewer2d.js b/web/app/sketcher/viewer2d.js index bb8047c7..52c35c82 100644 --- a/web/app/sketcher/viewer2d.js +++ b/web/app/sketcher/viewer2d.js @@ -121,7 +121,6 @@ class Viewer { var b = new EndPoint(x2, y2); var line = new Segment(a, b); layer.add(line); - line.stabilize(this); return line; };