diff --git a/modules/gems/traverse.js b/modules/gems/traverse.js new file mode 100644 index 00000000..8c401577 --- /dev/null +++ b/modules/gems/traverse.js @@ -0,0 +1,19 @@ + + +export function dfs(node, children, callback) { + const visited = new Set(); + const stack = []; + stack.push(node); + while (stack.length) { + const node = stack.pop(); + if (visited.has(node)) { + continue; + } + visited.add(node); + if (callback(node)) { + return; + } + children(node, child => stack.push(child)); + } +} + diff --git a/package.json b/package.json index 28b2c65c..3c5b4892 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "webpack-dev-server": "^3.7.2" }, "dependencies": { - "@dagrejs/graphlib": "^2.1.4", "classnames": "2.2.5", "clipper-lib": "6.2.1", "diff-match-patch": "1.0.0", diff --git a/web/app/sketcher/actions/constraintActions.js b/web/app/sketcher/actions/constraintActions.js index 7530b0cb..92012102 100644 --- a/web/app/sketcher/actions/constraintActions.js +++ b/web/app/sketcher/actions/constraintActions.js @@ -4,6 +4,7 @@ import {Circle} from "../shapes/circle"; import {Segment} from "../shapes/segment"; import {isInstanceOf, matchAll, matchTypes, sortSelectionByType} from "./matchUtils"; import constraints from "../../../test/cases/constraints"; +import {Arc} from "../shapes/arc"; export default [ @@ -29,7 +30,10 @@ export default [ { shortName: 'Tangent', description: 'Tangent Between Line And Circle', - selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, Circle, 1, Segment, 1), + selectionMatcher: [ + (selection, sortedByType) => matchTypes(sortedByType, Circle, 1, Segment, 1), + (selection, sortedByType) => matchTypes(sortedByType, Arc, 1, Segment, 1), + ], invoke: ctx => { @@ -146,6 +150,28 @@ export default [ } }, + { + shortName: 'Fillet', + description: 'Add a Fillet', + selectionMatcher: (selection) => { + if (matchTypes(selection, EndPoint, 1)) { + + } else { + return false; + } + }, + + invoke: ctx => { + const {viewer} = ctx; + + const [point] = viewer.selected; + + const constr = new AlgNumConstraint(ConstraintDefinitions.LockPoint, [point]); + constr.initConstants(); + editConstraint(ctx, constr, () => viewer.parametricManager.add(constr)); + } + }, + { shortName: 'Mirror', description: 'Mirror Objects', diff --git a/web/app/sketcher/actions/index.js b/web/app/sketcher/actions/index.js index 9634928b..2766c9d8 100644 --- a/web/app/sketcher/actions/index.js +++ b/web/app/sketcher/actions/index.js @@ -14,9 +14,19 @@ export function matchAvailableActions(selection) { if (selection.length) { for (let action of ALL_CONTEXTUAL_ACTIONS) { - if (action.selectionMatcher(selection, sortedByType)) { - matched.push(action); + + if (Array.isArray(action.selectionMatcher)) { + action.selectionMatcher.forEach(matcher => { + if (matcher(selection, sortedByType)) { + matched.push(action); + } + }) + } else { + if (action.selectionMatcher(selection, sortedByType)) { + matched.push(action); + } } + } } diff --git a/web/app/sketcher/constr/ANConstraints.js b/web/app/sketcher/constr/ANConstraints.js index 912d3271..d1aec98d 100644 --- a/web/app/sketcher/constr/ANConstraints.js +++ b/web/app/sketcher/constr/ANConstraints.js @@ -6,9 +6,9 @@ import {Types} from "../io"; import {Constraints} from "../constraints"; import Vector from "../../../../modules/math/vector"; -export const ConstraintDefinitions = indexById([ +export const ConstraintDefinitions = { - { + PCoincident : { id: 'PCoincident', name: 'Two Points Coincidence', @@ -35,7 +35,7 @@ export const ConstraintDefinitions = indexById([ }, - { + TangentLC: { id: 'TangentLC', name: 'Line & Circle Tangency', constants: { @@ -59,22 +59,11 @@ export const ConstraintDefinitions = indexById([ collectPolynomials: (polynomials, [ang, w, cx, cy, r], {inverted}) => { - polynomials.push(new Polynomial(0) - .monomial(1) - .term(cx, POW_1_FN) - .term(ang, COS_FN) - .monomial(1) - .term(cy, POW_1_FN) - .term(ang, SIN_FN) - .monomial(-1) - .term(w, POW_1_FN) - .monomial(- (inverted ? -1 : 1)) - .term(r, POW_1_FN) - ); + polynomials.push(tangentLCPolynomial(ang, w, cx, cy, r, inverted)); }, }, - { + PointOnLine: { id: 'PointOnLine', name: 'Point On Line', @@ -103,7 +92,7 @@ export const ConstraintDefinitions = indexById([ }, - { + DistancePP: { id: 'DistancePP', name: 'Distance Between Two Point', constants: { @@ -145,7 +134,7 @@ export const ConstraintDefinitions = indexById([ }, - { + Angle: { id: 'Angle', name: 'Absolute Line Angle', constants: { @@ -169,7 +158,7 @@ export const ConstraintDefinitions = indexById([ }, }, - { + AngleBetween: { id: 'AngleBetween', name: 'Angle Between Two Lines', constants: { @@ -198,7 +187,7 @@ export const ConstraintDefinitions = indexById([ }, }, - { + SegmentLength: { id: 'SegmentLength', name: 'Segment Length', constants: { @@ -225,7 +214,7 @@ export const ConstraintDefinitions = indexById([ }, }, - { + Polar: { id: 'Polar', name: 'Polar Coordinate', @@ -251,7 +240,7 @@ export const ConstraintDefinitions = indexById([ }, - { + LockPoint: { id: 'LockPoint', name: 'Lock Point', constants: { @@ -277,8 +266,60 @@ export const ConstraintDefinitions = indexById([ }, }, + ArcConsistency: { + id: 'ArcConsistency', + name: 'Arc Consistency', - { + defineParamsScope: ([arc], callback) => { + arc.visitParams(callback); + }, + + collectPolynomials: (polynomials, [r, ang1, ang2, ax, ay, bx, by, cx, cy]) => { + polynomials.push(new Polynomial() + .monomial(-1).term(ax, POW_1_FN) + .monomial().term(cx, POW_1_FN).monomial().term(r, POW_1_FN).term(ang1, COS_FN) ); + polynomials.push(new Polynomial() + .monomial(-1).term(ay, POW_1_FN) + .monomial().term(cy, POW_1_FN).monomial().term(r, POW_1_FN).term(ang1, SIN_FN) ); + + polynomials.push(new Polynomial() + .monomial(-1).term(bx, POW_1_FN) + .monomial().term(cx, POW_1_FN).monomial().term(r, POW_1_FN).term(ang2, COS_FN) ); + polynomials.push(new Polynomial() + .monomial(-1).term(by, POW_1_FN) + .monomial().term(cy, POW_1_FN).monomial().term(r, POW_1_FN).term(ang2, SIN_FN) ); + }, + }, + + Fillet: { + id: 'Fillet', + name: 'Fillet Between Two Lines', + + constants: { + inverted1: { + type: 'boolean', + initialValue: () => false, + }, + inverted2: { + type: 'boolean', + initialValue: () => false, + } + }, + + defineParamsScope: ([l1, l2, arc], callback) => { + l1.collectParams(callback); + l2.collectParams(callback); + arc.collectParams(callback); + }, + + collectPolynomials: (polynomials, [ang1, w1, cx1, cy1, r1, ang2, w2, cx2, cy2, r2], {inverted1, inverted2}) => { + polynomials.push(tangentLCPolynomial(ang1, w1, cx1, cy1, r1, inverted1)); + polynomials.push(tangentLCPolynomial(ang2, w2, cx2, cy2, r2, inverted2)); + }, + + }, + + Mirror: { id: 'Mirror', name: 'Mirror Objects', @@ -307,7 +348,22 @@ export const ConstraintDefinitions = indexById([ } -]); +}; + + +function tangentLCPolynomial(ang, w, cx, cy, r, inverted) { + return new Polynomial(0) + .monomial(1) + .term(cx, POW_1_FN) + .term(ang, COS_FN) + .monomial(1) + .term(cy, POW_1_FN) + .term(ang, SIN_FN) + .monomial(-1) + .term(w, POW_1_FN) + .monomial(- (inverted ? -1 : 1)) + .term(r, POW_1_FN); +} export class AlgNumConstraint { diff --git a/web/app/sketcher/constr/AlgNumSystem.js b/web/app/sketcher/constr/AlgNumSystem.js index 8f108788..79973657 100644 --- a/web/app/sketcher/constr/AlgNumSystem.js +++ b/web/app/sketcher/constr/AlgNumSystem.js @@ -14,7 +14,9 @@ export class AlgNumSubSystem { eliminatedParams = new Map(); polynomials = []; - substitutedParams = []; + substitutedParams = new Map(); + substitutionOrder = []; + polyToConstr = new Map(); conflicting = new Set(); @@ -66,25 +68,32 @@ export class AlgNumSubSystem { // this.conflicting.add(constraint); // this.redundant.add(constraint); } else { + constraint.objects.forEach(o => o.constraints.add(constraint)); this.updateFullyConstrainedObjects(); } } + invalidate() { + this.prepare(); + this.solveFine(); + this.updateFullyConstrainedObjects(); + } removeConstraint(constraint) { + this._removeConstraint(constraint); + this.invalidate(); + } + + _removeConstraint(constraint) { let index = this.allConstraints.indexOf(constraint); if (index !== -1) { this.allConstraints.splice(index, 1); this.conflicting.delete(constraint); this.redundant.delete(constraint); - this.prepare(); - this.prepare(); - this.solveFine(); - this.updateFullyConstrainedObjects(); + constraint.objects.forEach(o => o.constraints.delete(constraint)); } } - isConflicting(constraint) { return this.conflicting.has(constraint); } @@ -100,7 +109,8 @@ export class AlgNumSubSystem { reset() { this.polynomials = []; - this.substitutedParams = []; + this.substitutedParams.clear(); + this.substitutionOrder = []; this.eliminatedParams.clear(); this.polyToConstr.clear(); this.paramToIsolation.clear(); @@ -169,8 +179,9 @@ export class AlgNumSubSystem { polynomial.substitute(p1, p2, constant); } } - this.substitutedParams.push([p1, new Polynomial().monomial(constant).term(p2, POW_1_FN)]); + this.substitute(p1, new Polynomial().monomial(constant).term(p2, POW_1_FN)); this.polynomials[i] = null; + requirePass = true; } else { const b = - polynomial.constant / m1.constant; @@ -185,7 +196,7 @@ export class AlgNumSubSystem { } transaction.push(polyTransaction); transaction.push(() => { - this.substitutedParams.push([p1, new Polynomial(b).monomial(constant).term(p2, POW_1_FN)]); + this.substitute(p1, new Polynomial(b).monomial(constant).term(p2, POW_1_FN)); this.polynomials[i] = null; }); } @@ -208,11 +219,14 @@ export class AlgNumSubSystem { } + substitute(param, overPolynomial) { + this.substitutionOrder.push(param); + this.substitutedParams.set(param, overPolynomial); + } + prepare() { - this.isDirty = false; - this.reset(); this.evaluatePolynomials(); @@ -229,7 +243,7 @@ export class AlgNumSubSystem { }); console.log('with respect to:'); - this.substitutedParams.forEach(([x, expr]) => console.log('X' + x.id + ' = ' + expr.toString())); + this.substitutionOrder.forEach(x => console.log('X' + x.id + ' = ' + this.substitutedParams.get(x).toString())); } splitByIsolatedClusters(polynomials) { @@ -330,18 +344,15 @@ export class AlgNumSubSystem { p.set(val); } - for (let i = this.substitutedParams.length - 1; i >= 0; i--) { - let [param, expression] = this.substitutedParams[i]; + for (let i = this.substitutionOrder.length - 1; i >= 0; i--) { + const param = this.substitutionOrder[i]; + const expression = this.substitutedParams.get(param); param.set(expression.value()); } } updateFullyConstrainedObjects() { - - const substitutedParamsLookup = new Set(); - this.substitutedParams.forEach(([p]) => substitutedParamsLookup.add(p)); - this.validConstraints(c => { c.objects.forEach(obj => { @@ -349,11 +360,7 @@ export class AlgNumSubSystem { let allLocked = true; obj.visitParams(p => { - - const eliminated = this.eliminatedParams.has(p); - const substituted = substitutedParamsLookup.has(p); - const iso = this.paramToIsolation.get(p); - if (!eliminated && !substituted && (!iso || !iso.fullyConstrained)) { + if (!this.isParamFullyConstrained(p)) { allLocked = false; } }); @@ -363,6 +370,27 @@ export class AlgNumSubSystem { }); } + isParamShallowConstrained(p) { + const iso = this.paramToIsolation.get(p); + return this.eliminatedParams.has(p) || (iso && iso.fullyConstrained); + } + + isParamFullyConstrained(p) { + const stack = [p]; + while (stack.length) { + const param = stack.pop(); + if (!this.isParamShallowConstrained(param)) { + return false; + } + + const substitution = this.substitutedParams.get(p); + if (substitution) { + substitution.visitParams(p => stack.push(p)); + } + } + return true; + } + } diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index 91fee537..237c238a 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -207,10 +207,6 @@ IO.prototype._loadSketch = function(sketch) { if (boundaryNeedsUpdate) { this.addNewBoundaryObjects(boundary, maxEdge); } - const boundaryLayer = this.viewer.findLayerByName(IO.BOUNDARY_LAYER_NAME); - if (boundaryLayer != null) { - this.linkEndPoints(boundaryLayer.objects); - } let sketchConstraints = sketch['constraints']; if (sketchConstraints !== undefined) { @@ -233,23 +229,6 @@ IO.prototype._loadSketch = function(sketch) { } }; -IO.prototype.linkEndPoints = function(objects) { - const index = HashTable.forVector2d(); - for (let obj of objects) { - obj.accept((o) => { - if (o._class == Types.POINT) { - const equalPoint = index.get(o); - if (equalPoint == null) { - index.put(o, o); - } else { - o.linked.push(equalPoint); - equalPoint.linked.push(o); - } - } - return true; - }) - } -}; IO.prototype.synchLine = function(skobj, edgeObj) { skobj.a.x = edgeObj.a.x; diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 26e7fa8c..29159769 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -8,24 +8,20 @@ export {Constraints, ParametricManager} class ParametricManager { + algNumSystem = new AlgNumSubSystem(); + constantTable = {}; externalConstantResolver = null; - solveSystems; - $update = stream(); - $constraints = this.$update.map(layers => layers.reduce((all, layer) => { - layer.allConstraints.forEach(c => all.push(c)); - layer.modifiers.forEach(c => all.push(c)); - return all - }, []).sort((c1, c2) => c1.id - c2.id)).remember([]); + $constraints = this.$update + .map(() => [...this.algNumSystem.allConstraints, ...this.algNumSystem.modifiers].sort((c1, c2) => c1.id - c2.id)) + .remember([]); constructor(viewer) { this.viewer = viewer; - this.reset(); - this.viewer.params.define('constantDefinition', null); this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)(); this.constantResolver = this.createConstantResolver(); @@ -38,7 +34,7 @@ class ParametricManager { } reset() { - this.solveSystems = [new AlgNumSubSystem()]; + this.algNumSystem = new AlgNumSubSystem(); } addAlgNum(constr) { @@ -118,7 +114,7 @@ class ParametricManager { }; notify() { - this.$update.next(this.solveSystems); + this.$update.next(); }; commit() { @@ -126,43 +122,22 @@ class ParametricManager { this.viewer.refresh(); }; - getPlacementLayerIndex(objects) { - for (let i = this.solveSystems.length - 1; i >= 0; --i) { - - const system = this.solveSystems[i]; - - for (let o of objects) { - if (o.solveSystem === system) { - return i; - } - } - } - - return 0; - } - _add(constr) { - if (constr.modifier) { throw 'use addModifier instead'; } - - let system = this.solveSystems[this.getPlacementLayerIndex(constr.objects)]; - - for (let o of constr.objects) { - if (!o.solveSystem) { - o.solveSystem = system; - } - } - - system.addConstraint(constr); + this.algNumSystem.addConstraint(constr); }; + refresh() { + this.notify(); + this.viewer.refresh(); + } + add(constr) { this.viewer.historyManager.checkpoint(); this._add(constr); - this.notify(); - this.viewer.refresh(); + this.refresh(); }; addAll(constrs) { @@ -170,86 +145,38 @@ class ParametricManager { for (let i = 0; i < constrs.length; i++) { this._add(constrs[i]); } - this.notify(); - this.viewer.refresh(); + this.refresh(); }; remove(constr) { this.viewer.historyManager.checkpoint(); - // this.algNumSystem.removeConstraint(constr); + this.algNumSystem.removeConstraint(constr); this.refresh(); }; removeObjects(objects) { - throw 'implement me'; - - let toRemove = new Set(); - - objects.forEach(obj => obj.visitParams(p => { - this.algNumSystem.allConstraints.forEach(c => { - c.objects.forEach(o => { - - }) - }); - let constraints = this.system.paramToConstraintsIndex.get(p); - if (constraints) { - constraints.forEach(constr => { - toRemove.add(constr); - }); - } - })); objects.forEach(obj => { + obj.constraints.forEach(c => this.algNumSystem._removeConstraint(c)); if (obj.layer != null) { obj.layer.remove(obj); } }); - if (toRemove.size !== 0) { - // toRemove.forEach(constr => { - // this.cleanUpOnRemove(constr); - // }); - let survivedConstraints = []; - this.system.constraints.forEach(c => { - if (!toRemove.has(c)) { - survivedConstraints.push(c); - } - }); - this.system.setConstraints(survivedConstraints); - this.notify(); - } - if (dependants.length > 0) { - this.removeObjects(dependants); - } + this.algNumSystem.invalidate(); + this.refresh(); }; prepare() { - this.solveSystems.forEach(system => system.prepare()); + //backward comp. } solve(rough) { - this.solveSystems.forEach(system => system.solve(rough)); + this.algNumSystem.solve(rough); } addModifier(modifier) { - - modifier.managedObjects.forEach(o => { - if (o.solveSystem) { - throw 'adding modifiers to already constrained objects isn not supported yet'; - } - }); - - this.viewer.historyManager.checkpoint(); - let index = this.getPlacementLayerIndex(modifier.referenceObjects) + 1; - if (index === this.solveSystems.length) { - this.solveSystems.push(new AlgNumSubSystem()); - } - const solveSystem = this.solveSystems[index]; - solveSystem.modifiers.push(modifier); - - modifier.managedObjects.forEach(go => go.solveSystem = solveSystem); - - this.notify(); - this.viewer.refresh(); + this.algNumSystem.addModifier(modifier); + this.refresh(); } } diff --git a/web/app/sketcher/shapes/arc.js b/web/app/sketcher/shapes/arc.js index a9d1b69b..0c579311 100644 --- a/web/app/sketcher/shapes/arc.js +++ b/web/app/sketcher/shapes/arc.js @@ -1,10 +1,10 @@ -import * as utils from '../../utils/utils'; import * as math from '../../math/math'; import Vector from 'math/vector'; -import {Ref} from './ref' -import {Constraints} from '../parametric' -import {pointIterator, SketchObject} from './sketch-object'; -import {EndPoint} from "./point"; +import {SketchObject} from './sketch-object'; +import {Param} from "./param"; +import {greaterThanConstraint} from "../constr/barriers"; +import {MIN_RADIUS} from "./circle"; +import {AlgNumConstraint, ConstraintDefinitions} from "../constr/ANConstraints"; export class Arc extends SketchObject { @@ -17,16 +17,29 @@ export class Arc extends SketchObject { b.parent = this; c.parent = this; this.children.push(a, b, c); - this.r = new Ref(0); - this.r.value = this.distanceA(); - this.r.obj = this; + + this.r = new Param(MIN_RADIUS + 0.001); + this.r.constraints = [greaterThanConstraint(MIN_RADIUS)]; + + this.ang1 = new Param(0); + this.ang2 = new Param(0); + + this.syncGeometry(); + } + + syncGeometry() { + this.ang1.set(this.calcStartAng()); + this.ang2.set(this.calcEndAng()); + this.r.set(this.distanceA()); } visitParams(callback) { + callback(this.r); + callback(this.ang1); + callback(this.ang2); this.a.visitParams(callback); this.b.visitParams(callback); this.c.visitParams(callback); - callback(this.r); } getReferencePoint() { @@ -41,7 +54,7 @@ export class Arc extends SketchObject { radiusForDrawing() { - return this.distanceA(); + return this.r.get(); } distanceA() { @@ -51,20 +64,28 @@ export class Arc extends SketchObject { distanceB() { return math.distance(this.b.x, this.b.y, this.c.x, this.c.y); } - - getStartAngle() { + + calcStartAng() { return Math.atan2(this.a.y - this.c.y, this.a.x - this.c.x); } + + calcEndAng() { + return Math.atan2(this.b.y - this.c.y, this.b.x - this.c.x); + } + + getStartAngle() { + return this.ang1.get(); + } getEndAngle() { - return Math.atan2(this.b.y - this.c.y, this.b.x - this.c.x); + return this.ang2.get(); } drawImpl(ctx, scale) { ctx.beginPath(); - var r = this.radiusForDrawing(); - var startAngle = this.getStartAngle(); - var endAngle; + let r = this.radiusForDrawing(); + let startAngle = this.getStartAngle(); + let endAngle; if (math.areEqual(this.a.x, this.b.x, math.TOLERANCE) && math.areEqual(this.a.y, this.b.y, math.TOLERANCE)) { endAngle = startAngle + 2 * Math.PI; @@ -72,9 +93,9 @@ export class Arc extends SketchObject { endAngle = this.getEndAngle(); } ctx.arc(this.c.x, this.c.y, r, startAngle, endAngle); - var distanceB = this.distanceB(); + let distanceB = this.distanceB(); if (Math.abs(r - distanceB) * scale > 1) { - var adj = r / distanceB; + let adj = r / distanceB; ctx.save(); ctx.setLineDash([7 / scale]); ctx.lineTo(this.b.x, this.b.y); @@ -124,9 +145,10 @@ export class Arc extends SketchObject { } stabilize(viewer) { - this.r.set(this.distanceA()); - viewer.parametricManager._add(new Constraints.P2PDistanceV(this.b, this.c, this.r)); - viewer.parametricManager._add(new Constraints.P2PDistanceV(this.a, this.c, this.r)); + this.syncGeometry(); + const constr = new AlgNumConstraint(ConstraintDefinitions.ArcConsistency, [this]); + constr.internal = true; + viewer.parametricManager._add(constr); } copy() { diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index d26cdec0..2982cf85 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -3,7 +3,7 @@ import {SketchObject} from './sketch-object' import {Param} from "./param"; import {greaterThanConstraint} from "../constr/barriers"; -const MIN_RADIUS = 100; +export const MIN_RADIUS = 100; export class Circle extends SketchObject { diff --git a/web/app/sketcher/shapes/point.js b/web/app/sketcher/shapes/point.js index ff6bcab7..4873d202 100644 --- a/web/app/sketcher/shapes/point.js +++ b/web/app/sketcher/shapes/point.js @@ -1,6 +1,5 @@ import {SketchObject} from './sketch-object' import {DrawPoint} from './draw-utils' -import {Generator} from '../id-generator' import Vector from 'math/vector'; import {Param} from "./param"; diff --git a/web/app/sketcher/shapes/segment.js b/web/app/sketcher/shapes/segment.js index 37104040..25148e73 100644 --- a/web/app/sketcher/shapes/segment.js +++ b/web/app/sketcher/shapes/segment.js @@ -58,8 +58,8 @@ export class Segment extends SketchObject { const c2 = new AlgNumConstraint(ConstraintDefinitions.Polar, [this, this.a, this.b]); c1.internal = true; c2.internal = true; - viewer.parametricManager.addAlgNum(c1); - viewer.parametricManager.addAlgNum(c2); + viewer.parametricManager._add(c1); + viewer.parametricManager._add(c2); } recoverIfNecessary() { diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index ea51f962..66181d16 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -2,6 +2,8 @@ import {Generator} from '../id-generator' import {Shape} from './shape' import {Types} from '../io'; import {Styles} from "../styles"; +import {dfs} from 'gems/traverse'; +import {ConstraintDefinitions} from "../constr/ANConstraints"; export class SketchObject extends Shape { constructor() { @@ -9,11 +11,9 @@ export class SketchObject extends Shape { this.id = Generator.genID(); this.marked = null; this.children = []; - this.linked = []; this.layer = null; this.fullyConstrained = false; - this.managedBy = null; - this.solveSystem = null; + this.constraints = new Set(); } normalDistance(aim, scale) { @@ -43,32 +43,28 @@ export class SketchObject extends Shape { recoverIfNecessary() { return false; } - - isAuxOrLinkedTo() { - if (!!this.aux) { - return true; - } - for (var i = 0; i < this.linked.length; ++i) { - if (!!this.linked[i].aux) { - return true; + + visitLinked(cb) { + dfs(this, (obj, chCb) => obj.constraints.forEach(c => { + if (c.schema.id === ConstraintDefinitions.PCoincident.id) { + c.objects.forEach(chCb); } - } - return false; + }), cb); } - + _translate(dx, dy, translated) { translated[this.id] = 'x'; - for (var i = 0; i < this.linked.length; ++i) { - if (translated[this.linked[i].id] != 'x') { - this.linked[i]._translate(dx, dy, translated); + this.visitLinked(l => { + if (translated[l.id] !== 'x') { + l._translate(dx, dy, translated); } - } + }); this.translateImpl(dx, dy); }; translate(dx, dy) { // this.translateImpl(dx, dy); - if (this.isAuxOrLinkedTo()) { + if (this.fullyConstrained) { return; } this._translate(dx, dy, {}); diff --git a/web/app/sketcher/tools/arc.js b/web/app/sketcher/tools/arc.js index 4dfe3422..76c5ed3c 100644 --- a/web/app/sketcher/tools/arc.js +++ b/web/app/sketcher/tools/arc.js @@ -34,6 +34,7 @@ export class AddArcTool extends Tool { } else { this.demoSecondPoint(); } + this.arc.syncGeometry(); this.viewer.snap(p.x, p.y, [this.arc.a, this.arc.b, this.arc.c]); this.viewer.refresh(); @@ -80,7 +81,7 @@ export class AddArcTool extends Tool { finishStep() { this.arc.stabilize(this.viewer); this.pointPicked(this.arc.b.x, this.arc.b.y); - this.viewer.refresh(); + this.viewer.parametricManager.refresh(); this.viewer.toolManager.releaseControl(); } diff --git a/web/app/sketcher/tools/drag.js b/web/app/sketcher/tools/drag.js index 27d5d74d..b6542f10 100644 --- a/web/app/sketcher/tools/drag.js +++ b/web/app/sketcher/tools/drag.js @@ -75,7 +75,7 @@ export class DragTool extends Tool { getParamsToLock() { var params = []; this.obj.accept(function (obj) { - if (obj._class === 'TCAD.TWO.EndPoint' && !obj.isAuxOrLinkedTo()) { + if (obj._class === 'TCAD.TWO.EndPoint' && !obj.fullyConstrained) { params.push(obj._x); params.push(obj._y); } diff --git a/web/app/sketcher/tools/pan.js b/web/app/sketcher/tools/pan.js index a34c803e..ce0f7eec 100644 --- a/web/app/sketcher/tools/pan.js +++ b/web/app/sketcher/tools/pan.js @@ -38,7 +38,7 @@ export class BasePanTool extends Tool { } } this.viewer.select([toSelect], true); - if (!toSelect.isAuxOrLinkedTo()) { + if (!toSelect.fullyConstrained) { const tool = GetShapeEditTool(this.viewer, toSelect, e.altKey); tool.mousedown(e); this.viewer.toolManager.switchTool(tool);