From 4713cc1fc81559d12a42d9c55f1a5d3f93cf3f4c Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sat, 1 Jun 2019 00:33:14 -0700 Subject: [PATCH] sketcher sub systems split --- modules/gems/iterables.js | 20 + package-lock.json | 8 + package.json | 1 + web/app/sketcher.js | 4 +- web/app/sketcher/constr/solver.js | 10 +- web/app/sketcher/constraints.js | 1091 +++++++++++++++ web/app/sketcher/io.js | 7 +- web/app/sketcher/mirror.js | 16 + web/app/sketcher/parametric.js | 1199 +++-------------- web/app/sketcher/shapes/arc.js | 22 +- web/app/sketcher/shapes/bezier-curve.js | 10 +- web/app/sketcher/shapes/circle.js | 8 +- web/app/sketcher/shapes/dim.js | 8 +- web/app/sketcher/shapes/ellipse.js | 10 +- web/app/sketcher/shapes/nurbsObject.js | 6 +- web/app/sketcher/shapes/point.js | 26 +- web/app/sketcher/shapes/segment.js | 18 +- web/app/sketcher/shapes/sketch-object.js | 43 + web/app/sketcher/sketcher-app.js | 12 + web/app/sketcher/styles.js | 6 +- web/app/sketcher/system.js | 290 ++++ web/app/sketcher/tools/circle.js | 1 - web/app/sketcher/tools/drag.js | 2 - web/app/sketcher/tools/ellipse.js | 1 - web/app/sketcher/tools/fillet.js | 34 +- web/app/sketcher/tools/manager.js | 4 +- web/app/sketcher/viewer2d.js | 14 +- web/sketcher.html | 3 +- web/test/cases/solveSystems.js | 216 +++ web/test/modes.js | 7 + web/test/runner.html | 1 + web/test/runner.less | 1 + web/test/suites.js | 1 + web/test/test.js | 12 +- web/test/utils/asserts.js | 10 +- web/test/utils/sketcher-utils.js | 15 +- .../subjects/modeller/sketcherUISubject.js | 48 +- webpack.config.js | 68 +- 38 files changed, 2063 insertions(+), 1190 deletions(-) create mode 100644 web/app/sketcher/constraints.js create mode 100644 web/app/sketcher/mirror.js create mode 100644 web/app/sketcher/system.js create mode 100644 web/test/cases/solveSystems.js diff --git a/modules/gems/iterables.js b/modules/gems/iterables.js index 4c43a9e2..4f5f5fad 100644 --- a/modules/gems/iterables.js +++ b/modules/gems/iterables.js @@ -56,6 +56,26 @@ export function addToListInMap(map, key, value) { list.push(value); } +export function addToSetInMap(map, key, value) { + let set = map.get(key); + if (!set) { + set = new Set(); + map.set(key, set); + } + set.add(value); +} + +export function removeFromSetInMap(map, key, value) { + let set = map.get(key); + if (set) { + set.delete(value); + if (set.size === 0) { + map.delete(key); + } + } +} + + export function removeInPlace(arr, val) { let index = arr.indexOf(val); if (index !== -1) { diff --git a/package-lock.json b/package-lock.json index 4a1bc556..aacc52af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1153,6 +1153,14 @@ } } }, + "@dagrejs/graphlib": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.1.4.tgz", + "integrity": "sha512-QCg9sL4uhjn468FDEsb/S9hS2xUZSrv/+dApb1Ze5VKO96pTXKNJZ6MGhIpgWkc1TVhbVGH9/7rq/Mf8/jWicw==", + "requires": { + "lodash": "^4.11.1" + } + }, "@webassemblyjs/ast": { "version": "1.7.11", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", diff --git a/package.json b/package.json index 7a731b30..ff8cb0cb 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "webpack-dev-server": "^3.1.14" }, "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.js b/web/app/sketcher.js index a3d8129f..2c34f545 100644 --- a/web/app/sketcher.js +++ b/web/app/sketcher.js @@ -51,8 +51,8 @@ function initializeSketcherApplication() { var constrList = new ui.List('constrs', { items : function() { var theItems = []; - for (var j = 0; j < pm.subSystems.length; j++) { - var sub = pm.subSystems[j]; + for (var j = 0; j < pm.system.subSystems.length; j++) { + var sub = pm.system.subSystems[j]; for (var i = 0; i < sub.constraints.length; ++i) { var constr = sub.constraints[i]; if (constr.aux !== true && app.constraintFilter[constr.NAME] != true) { diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index b0b3ddfa..e0277a47 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -2,7 +2,7 @@ import * as utils from '../../utils/utils' import * as math from '../../math/math' import QR from '../../math/qr' import LMOptimizer from '../../math/lm' -import {ConstantWrapper, EqualsTo} from './constraints' +import {ConstantWrapper, EqualsTo} from './solverConstraints' import {dog_leg} from '../../math/optim' import {newVector} from '../../math/vec'; @@ -17,8 +17,8 @@ Param.prototype.reset = function(value) { this.aux = false; }; -Param.prototype.set = function(value) { - if (this.aux) return; +Param.prototype.set = function(value, force) { + if (this.aux && !force) return; this.value = value; }; @@ -139,7 +139,9 @@ System.prototype.calcGrad = function(out) { var cParams = c.params; var grad = []; utils.fillArray(grad, 0, cParams.length, 0); - for (var p = 0; p < cParams.length; p++) { + c.gradient(grad); + + for (var p = 0; p < cParams.length; p++) { var param = cParams[p]; var j = param.j; out[j] += this.constraints[i].error() * grad[p]; // (10.4) diff --git a/web/app/sketcher/constraints.js b/web/app/sketcher/constraints.js new file mode 100644 index 00000000..dbb49060 --- /dev/null +++ b/web/app/sketcher/constraints.js @@ -0,0 +1,1091 @@ +import {Ref} from './shapes/ref'; +import * as math from '../math/math'; +import Vector from '../../../modules/math/vector'; + +class AbstractConstraint { + +} + +export const Constraints = { + Factory: {} +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +export class Coincident extends AbstractConstraint { + + reducible = true; + + static deserialize(refs, data) { + return new Coincident(refs(data[0]), refs(data[1])); + }; + + constructor(a, b) { + super(); + this.a = a; + this.b = b; + a.linked.push(b); + b.linked.push(a); + }; + + + getSolveData() { + return [ + ['equal', [this.a._x, this.b._x], []], + ['equal', [this.a._y, this.b._y], []] + ]; + } + + serialize() { + return [this.NAME, [this.a.id, this.b.id]]; + } + + getObjects() { + return [this.a, this.b]; + } +} + + + +Constraints.Coincident = Coincident; +Constraints.Coincident.prototype.NAME = 'coi'; +Constraints.Coincident.prototype.UI_NAME = 'Coincident'; + +Constraints.Factory[Coincident.prototype.NAME] = Coincident.deserialize; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.RadiusOffset = function(arc1, arc2, offset) { + this.arc1 = arc1; + this.arc2 = arc2; + this.offset = offset; +}; + +Constraints.RadiusOffset.prototype.NAME = 'RadiusOffset'; +Constraints.RadiusOffset.prototype.UI_NAME = 'Radius Offset'; + +Constraints.RadiusOffset.prototype.getSolveData = function(resolver) { + return [ + ['Diff', [this.arc1.r, this.arc2.r], [resolver(this.offset)]] + ]; +}; + +Constraints.RadiusOffset.prototype.serialize = function() { + return [this.NAME, [this.arc1.id, this.arc2.id, this.offset]]; +}; + +Constraints.Factory[Constraints.RadiusOffset.prototype.NAME] = function(refs, data) { + return new Constraints.RadiusOffset(refs(data[0]), refs(data[1]), data[2]); +}; + +Constraints.RadiusOffset.prototype.getObjects = function() { + return [this.arc1, this.arc2]; +}; + +Constraints.RadiusOffset.prototype.SettableFields = {'offset' : "Enter the offset"}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Lock = function(p, c) { + this.p = p; + this.c = c; +}; + +Constraints.Lock.prototype.NAME = 'lock'; +Constraints.Lock.prototype.UI_NAME = 'Lock'; + +Constraints.Lock.prototype.getSolveData = function() { + return [ + ['equalsTo', [this.p._x], [this.c.x]], + ['equalsTo', [this.p._y], [this.c.y]] + ]; +}; + +Constraints.Lock.prototype.serialize = function() { + return [this.NAME, [this.p.id, this.c]]; +}; + +Constraints.Factory[Constraints.Lock.prototype.NAME] = function(refs, data) { + return new Constraints.Lock(refs(data[0]), data[1]); +}; + + +Constraints.Lock.prototype.getObjects = function() { + return [this.p]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Parallel = function(l1, l2) { + this.l1 = l1; + this.l2 = l2; +}; + +Constraints.Parallel.prototype.NAME = 'parallel'; +Constraints.Parallel.prototype.UI_NAME = 'Parallel'; + +Constraints.Parallel.prototype.getSolveData = function() { + var params = []; + this.l1.collectParams(params); + this.l2.collectParams(params); + return [[this.NAME, params, []]]; +}; + +Constraints.Parallel.prototype.serialize = function() { + return [this.NAME, [this.l1.id, this.l2.id]]; +}; + +Constraints.Factory[Constraints.Parallel.prototype.NAME] = function(refs, data) { + return new Constraints.Parallel(refs(data[0]), refs(data[1])); +}; + +Constraints.Parallel.prototype.getObjects = function() { + return [this.l1, this.l2]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Perpendicular = function(l1, l2) { + this.l1 = l1; + this.l2 = l2; +}; + +Constraints.Perpendicular.prototype.NAME = 'perpendicular'; +Constraints.Perpendicular.prototype.UI_NAME = 'Perpendicular'; + +Constraints.Perpendicular.prototype.getSolveData = function() { + var params = []; + this.l1.collectParams(params); + this.l2.collectParams(params); + return [[this.NAME, params, []]]; +}; + +Constraints.Perpendicular.prototype.serialize = function() { + return [this.NAME, [this.l1.id, this.l2.id]]; +}; + +Constraints.Factory[Constraints.Perpendicular.prototype.NAME] = function(refs, data) { + return new Constraints.Perpendicular(refs(data[0]), refs(data[1])); +}; + +Constraints.Perpendicular.prototype.getObjects = function() { + return [this.l1, this.l2]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @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; + this.l = l; + this.d = d; +}; + +Constraints.P2LDistance.prototype.NAME = 'P2LDistance'; +Constraints.P2LDistance.prototype.UI_NAME = 'Distance P & L'; + +Constraints.P2LDistance.prototype.getSolveData = function(resolver) { + var params = []; + this.p.collectParams(params); + this.l.collectParams(params); + return [[this.NAME, params, [resolver(this.d)]]]; +}; + +Constraints.P2LDistance.prototype.serialize = function() { + return [this.NAME, [this.p.id, this.l.id, this.d]]; +}; + +Constraints.Factory[Constraints.P2LDistance.prototype.NAME] = function(refs, data) { + return new Constraints.P2LDistance(refs(data[0]), refs(data[1]), data[2]); +}; + +Constraints.P2LDistance.prototype.getObjects = function() { + return [this.p, this.l]; +}; + +Constraints.P2LDistance.prototype.SettableFields = {'d' : "Enter the distance"}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.MinLength = function(a, b, min) { + this.a = a; + this.b = b; + this.min = min; +}; + +Constraints.MinLength.prototype.aux = true; +Constraints.MinLength.prototype.NAME = 'MinLength'; +Constraints.MinLength.prototype.UI_NAME = 'MinLength'; + +Constraints.MinLength.prototype.getSolveData = function() { + var params = []; + this.a.collectParams(params); + this.b.collectParams(params); + return [[this.NAME, params, [this.min]]]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.P2LDistanceV = function(p, l, d) { + this.p = p; + this.l = l; + this.d = d; +}; + +Constraints.P2LDistanceV.prototype.aux = true; +Constraints.P2LDistanceV.prototype.NAME = 'P2LDistanceV'; +Constraints.P2LDistanceV.prototype.UI_NAME = 'Distance P & L'; + +Constraints.P2LDistanceV.prototype.getSolveData = function() { + var params = []; + this.p.collectParams(params); + this.l.collectParams(params); + params.push(this.d); + return [[this.NAME, params]]; +}; + +// We don't serialize auxiliary constraints +// +//Constraints.P2LDistanceV.prototype.serialize = function() { +// return [this.NAME, [this.p.id, this.l.id, this.d.id]]; +//}; +// +//Constraints.Factory[Constraints.P2LDistanceV.prototype.NAME] = function(refs, data) { +// return new Constraints.P2LDistanceV(refs(data[0]), refs(data[1]), refs(data[2])); +//}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.P2PDistance = function(p1, p2, d) { + this.p1 = p1; + this.p2 = p2; + this.d = d; +}; + +Constraints.P2PDistance.prototype.NAME = 'P2PDistance'; +Constraints.P2PDistance.prototype.UI_NAME = 'Distance Points'; + +Constraints.P2PDistance.prototype.getSolveData = function(resolver) { + var params = []; + this.p1.collectParams(params); + this.p2.collectParams(params); + return [[this.NAME, params, [resolver(this.d)]]]; +}; + +Constraints.P2PDistance.prototype.serialize = function() { + return [this.NAME, [this.p1.id, this.p2.id, this.d]]; +}; + +Constraints.Factory[Constraints.P2PDistance.prototype.NAME] = function(refs, data) { + return new Constraints.P2PDistance(refs(data[0]), refs(data[1]), data[2]); +}; + +Constraints.P2PDistance.prototype.getObjects = function() { + return [this.p1, this.p2]; +}; + +Constraints.P2PDistance.prototype.SettableFields = {'d' : "Enter the distance"}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.P2PDistanceV = function(p1, p2, d) { + this.p1 = p1; + this.p2 = p2; + this.d = d; +}; + +Constraints.P2PDistanceV.prototype.aux = true; +Constraints.P2PDistanceV.prototype.NAME = 'P2PDistanceV'; +Constraints.P2PDistanceV.prototype.UI_NAME = 'Distance Points'; + +Constraints.P2PDistanceV.prototype.getSolveData = function() { + var params = []; + this.p1.collectParams(params); + this.p2.collectParams(params); + params.push(this.d); + return [[this.NAME, params]]; +}; + +// We don't serialize auxiliary constraints +// +//Constraints.P2PDistanceV.prototype.serialize = function() { +// return [this.NAME, [this.p1.id, this.p2.id, this.d.id]]; +//}; +// +//Constraints.Factory[Constraints.P2PDistanceV.prototype.NAME] = function(refs, data) { +// return new Constraints.P2PDistanceV(refs(data[0]), refs(data[1]), refs(data[2])); +//}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.GreaterThan = function(p, limit) { + this.p = p; + this.limit = limit; +}; + +Constraints.GreaterThan.prototype.aux = true; +Constraints.GreaterThan.prototype.NAME = 'GreaterThan'; +Constraints.GreaterThan.prototype.UI_NAME = 'Greater Than'; + +Constraints.GreaterThan.prototype.getSolveData = function() { + return [[this.NAME, [this.p], [this.limit]]]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Radius = function(arc, d) { + this.arc = arc; + this.d = d; +}; + +Constraints.Radius.prototype.NAME = 'Radius'; +Constraints.Radius.prototype.UI_NAME = 'Radius Value'; + + +Constraints.Radius.prototype.getSolveData = function(resolver) { + return [['equalsTo', [this.arc.r], [resolver(this.d)]]]; +}; + +Constraints.Radius.prototype.serialize = function() { + return [this.NAME, [this.arc.id, this.d]]; +}; + +Constraints.Factory[Constraints.Radius.prototype.NAME] = function(refs, data) { + return new Constraints.Radius(refs(data[0]), data[1]); +}; + +Constraints.Radius.prototype.getObjects = function() { + return [this.arc]; +}; + +Constraints.Radius.prototype.SettableFields = {'d' : "Enter the radius value"}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.RR = function(arc1, arc2) { + this.arc1 = arc1; + this.arc2 = arc2; +}; + +Constraints.RR.prototype.NAME = 'RR'; +Constraints.RR.prototype.UI_NAME = 'Radius Equality'; +//Constraints.RR.prototype.reducible = true; + + +Constraints.RR.prototype.getSolveData = function() { + return [['equal', [this.arc1.r, this.arc2.r], []]]; +}; + +Constraints.RR.prototype.serialize = function() { + return [this.NAME, [this.arc1.id, this.arc2.id]]; +}; + +Constraints.Factory[Constraints.RR.prototype.NAME] = function(refs, data) { + return new Constraints.RR(refs(data[0]), refs(data[1])); +}; + +Constraints.RR.prototype.getObjects = function() { + return [this.arc1, this.arc2]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.LL = function(line1, line2) { + this.line1 = line1; + this.line2 = line2; + this.length = new Ref(math.distanceAB(line1.a, line1.b)); +}; + +Constraints.LL.prototype.NAME = 'LL'; +Constraints.LL.prototype.UI_NAME = 'Lines Equality'; + +Constraints.LL.prototype.getSolveData = function() { + var params1 = []; + var params2 = []; + this.line1.collectParams(params1); + this.line2.collectParams(params2); + params1.push(this.length); + params2.push(this.length); + return [ + ['P2PDistanceV', params1, []], + ['P2PDistanceV', params2, []] + ]; +}; + +Constraints.LL.prototype.serialize = function() { + return [this.NAME, [this.line1.id, this.line2.id]]; +}; + +Constraints.Factory[Constraints.LL.prototype.NAME] = function(refs, data) { + return new Constraints.LL(refs(data[0]), refs(data[1])); +}; + +Constraints.LL.prototype.getObjects = function() { + return [this.line1, this.line2]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Vertical = function(line) { + this.line = line; +}; + +Constraints.Vertical.prototype.NAME = 'Vertical'; +Constraints.Vertical.prototype.UI_NAME = 'Vertical'; +//Constraints.Vertical.prototype.reducible = true; + +Constraints.Vertical.prototype.getSolveData = function() { + return [['equal', [this.line.a._x, this.line.b._x], []]]; +}; + +Constraints.Vertical.prototype.serialize = function() { + return [this.NAME, [this.line.id]]; +}; + +Constraints.Factory[Constraints.Vertical.prototype.NAME] = function(refs, data) { + return new Constraints.Vertical(refs(data[0])); +}; + +Constraints.Vertical.prototype.getObjects = function() { + return [this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Horizontal = function(line) { + this.line = line; +}; + +Constraints.Horizontal.prototype.NAME = 'Horizontal'; +Constraints.Horizontal.prototype.UI_NAME = 'Horizontal'; +//Constraints.Horizontal.prototype.reducible = true; + +Constraints.Horizontal.prototype.getSolveData = function() { + return [['equal', [this.line.a._y, this.line.b._y], []]]; +}; + +Constraints.Horizontal.prototype.serialize = function() { + return [this.NAME, [this.line.id]]; +}; + +Constraints.Factory[Constraints.Horizontal.prototype.NAME] = function(refs, data) { + return new Constraints.Horizontal(refs(data[0])); +}; + +Constraints.Horizontal.prototype.getObjects = function() { + return [this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Tangent = function(arc, line) { + this.arc = arc; + this.line = line; +}; + +Constraints.Tangent.prototype.NAME = 'Tangent'; +Constraints.Tangent.prototype.UI_NAME = 'Tangent'; + +Constraints.Tangent.prototype.getSolveData = function() { + var params = []; + this.arc.c.collectParams(params); + this.line.collectParams(params); + params.push(this.arc.r); + return [['P2LDistanceV', params, []]]; +}; + +Constraints.Tangent.prototype.serialize = function() { + return [this.NAME, [this.arc.id, this.line.id]]; +}; + +Constraints.Factory[Constraints.Tangent.prototype.NAME] = function(refs, data) { + return new Constraints.Tangent(refs(data[0]), refs(data[1])); +}; + +Constraints.Tangent.prototype.getObjects = function() { + return [this.arc, this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointOnLine = function(point, line) { + this.point = point; + this.line = line; +}; + +Constraints.PointOnLine.prototype.NAME = 'PointOnLine'; +Constraints.PointOnLine.prototype.UI_NAME = 'Point On Line'; + +Constraints.PointOnLine.prototype.getSolveData = function() { + var params = []; + this.point.collectParams(params); + this.line.collectParams(params); + return [['P2LDistance', params, [0]]]; +}; + +Constraints.PointOnLine.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.line.id]]; +}; + +Constraints.Factory[Constraints.PointOnLine.prototype.NAME] = function(refs, data) { + return new Constraints.PointOnLine(refs(data[0]), refs(data[1])); +}; + +Constraints.PointOnLine.prototype.getObjects = function() { + return [this.point, this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointOnArc = function(point, arc) { + this.point = point; + this.arc = arc; +}; + +Constraints.PointOnArc.prototype.NAME = 'PointOnArc'; +Constraints.PointOnArc.prototype.UI_NAME = 'Point On Arc'; + +Constraints.PointOnArc.prototype.getSolveData = function() { + var params = []; + this.point.collectParams(params); + this.arc.c.collectParams(params); + params.push(this.arc.r); + return [['P2PDistanceV', params, []]]; +}; + +Constraints.PointOnArc.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.arc.id]]; +}; + +Constraints.Factory[Constraints.PointOnArc.prototype.NAME] = function(refs, data) { + return new Constraints.PointOnArc(refs(data[0]), refs(data[1])); +}; + +Constraints.PointOnArc.prototype.getObjects = function() { + return [this.point, this.arc]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointOnCurve = function(point, curveObject) { + this.point = point; + this.curveObject = curveObject; +}; + +Constraints.PointOnCurve.prototype.NAME = 'PointOnCurve'; +Constraints.PointOnCurve.prototype.UI_NAME = 'Point On Curve'; + +Constraints.PointOnCurve.prototype.getSolveData = function() { + const params = []; + this.point.collectParams(params); + return [['PointOnCurve', params, [this.curveObject.curve]]]; +}; + +Constraints.PointOnCurve.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.curveObject.id]]; +}; + +Constraints.Factory[Constraints.PointOnCurve.prototype.NAME] = function(refs, data) { + return new Constraints.PointOnCurve(refs(data[0]), refs(data[1])); +}; + +Constraints.PointOnCurve.prototype.getObjects = function() { + return [this.point, this.curveObject]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointOnEllipseInternal = function(point, ellipse) { + this.point = point; + this.ellipse= ellipse; +}; + +Constraints.PointOnEllipseInternal.prototype.NAME = 'PointOnEllipseI'; +Constraints.PointOnEllipseInternal.prototype.UI_NAME = 'Point On Ellipse'; +Constraints.PointOnEllipseInternal.prototype.aux = true; + +Constraints.PointOnEllipseInternal.prototype.getSolveData = function() { + var params = []; + this.point.collectParams(params); + this.ellipse.ep1.collectParams(params); + this.ellipse.ep2.collectParams(params); + params.push(this.ellipse.r); + return [['PointOnEllipse', params, []]]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointOnEllipse = function(point, ellipse) { + Constraints.PointOnEllipseInternal.call(this, point, ellipse); +}; + +Constraints.PointOnEllipse.prototype.NAME = 'PointOnEllipse'; +Constraints.PointOnEllipse.prototype.UI_NAME = 'Point On Ellipse'; + +Constraints.PointOnEllipse.prototype.getSolveData = function() { + return Constraints.PointOnEllipseInternal.prototype.getSolveData.call(this); +}; + +Constraints.PointOnEllipse.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.ellipse.id]]; +}; + +Constraints.Factory[Constraints.PointOnEllipse.prototype.NAME] = function(refs, data) { + return new Constraints.PointOnEllipse(refs(data[0]), refs(data[1])); +}; + +Constraints.PointOnEllipse.prototype.getObjects = function() { + return [this.point, this.ellipse]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.EllipseTangent = function(line, ellipse) { + this.line = line; + this.ellipse = ellipse; +}; + +Constraints.EllipseTangent.prototype.NAME = 'EllipseTangent'; +Constraints.EllipseTangent.prototype.UI_NAME = 'Tangent Ellipse'; + +Constraints.EllipseTangent.prototype.getSolveData = function() { + const params = []; + this.line.collectParams(params); + this.ellipse.ep1.collectParams(params); + this.ellipse.ep2.collectParams(params); + params.push(this.ellipse.r); + return [['EllipseTangent', params, []]]; + +}; + +Constraints.EllipseTangent.prototype.serialize = function() { + return [this.NAME, [this.line.id, this.ellipse.id]]; +}; + +Constraints.Factory[Constraints.EllipseTangent.prototype.NAME] = function(refs, data) { + return new Constraints.EllipseTangent(refs(data[0]), refs(data[1])); +}; + +Constraints.EllipseTangent.prototype.getObjects = function() { + return [this.line, this.ellipse]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.CurveTangent = function(line, curveObject) { + this.line = line; + this.curveObject = curveObject; + let [uMin, uMax] = this.curveObject.curve.domain(); + let initPoint = this.curveObject.curve.point(0.5 * (uMin + uMax)); + this.tx = new Ref(initPoint[0]); + this.ty = new Ref(initPoint[1]); +}; + +Constraints.CurveTangent.prototype.NAME = 'CurveTangent'; +Constraints.CurveTangent.prototype.UI_NAME = 'Curve Curve'; + +Constraints.CurveTangent.prototype.getSolveData = function() { + const params = []; + this.line.collectParams(params); + params.push(this.tx); + params.push(this.ty); + return [['CurveTangent', params, [this.curveObject.curve]]]; + +}; + +Constraints.CurveTangent.prototype.serialize = function() { + return [this.NAME, [this.line.id, this.curveObject.id]]; +}; + +Constraints.Factory[Constraints.CurveTangent.prototype.NAME] = function(refs, data) { + return new Constraints.CurveTangent(refs(data[0]), refs(data[1])); +}; + +Constraints.CurveTangent.prototype.getObjects = function() { + return [this.line, this.curveObject]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.PointInMiddle = function(point, line) { + this.point = point; + this.line = line; + this.length = new Ref(math.distanceAB(line.a, line.b) / 2); +}; + +Constraints.PointInMiddle.prototype.NAME = 'PointInMiddle'; +Constraints.PointInMiddle.prototype.UI_NAME = 'Point In the Middle'; + +Constraints.PointInMiddle.prototype.getSolveData = function() { + var params1 = []; + var params2 = []; + + this.line.a.collectParams(params1); + this.point.collectParams(params1); + params1.push(this.length); + + this.line.b.collectParams(params2); + this.point.collectParams(params2); + params2.push(this.length); + + return [ + ['P2PDistanceV', params1, []], + ['P2PDistanceV', params2, []] + ]; +}; + +Constraints.PointInMiddle.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.line.id]]; +}; + +Constraints.Factory[Constraints.PointInMiddle.prototype.NAME] = function(refs, data) { + return new Constraints.PointInMiddle(refs(data[0]), refs(data[1])); +}; + +Constraints.PointInMiddle.prototype.getObjects = function() { + return [this.point, this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Symmetry = function(point, line) { + this.point = point; + this.line = line; + this.length = new Ref(math.distanceAB(line.a, line.b) / 2); +}; + +Constraints.Symmetry.prototype.NAME = 'Symmetry'; +Constraints.Symmetry.prototype.UI_NAME = 'Symmetry'; + +Constraints.Symmetry.prototype.getSolveData = function(resolver) { + var pointInMiddleData = Constraints.PointInMiddle.prototype.getSolveData.call(this, [resolver]); + var pointOnLineData = Constraints.PointOnLine.prototype.getSolveData.call(this, [resolver]); + return pointInMiddleData.concat(pointOnLineData); +}; + +Constraints.Symmetry.prototype.serialize = function() { + return [this.NAME, [this.point.id, this.line.id]]; +}; + +Constraints.Factory[Constraints.Symmetry.prototype.NAME] = function(refs, data) { + return new Constraints.Symmetry(refs(data[0]), refs(data[1])); +}; + +Constraints.Symmetry.prototype.getObjects = function() { + return [this.point, this.line]; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Angle = function(p1, p2, p3, p4, angle) { + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.p4 = p4; + this._angle = new Ref(0); + this.angle = angle; +}; + +Constraints.Angle.prototype.NAME = 'Angle'; +Constraints.Angle.prototype.UI_NAME = 'Lines Angle'; + +Constraints.Angle.prototype.getSolveData = function(resolver) { + this._angle.set(resolver(this.angle) / 180 * Math.PI); + var params = []; + this.p1.collectParams(params); + this.p2.collectParams(params); + this.p3.collectParams(params); + this.p4.collectParams(params); + params.push(this._angle); + return [['angleConst', params, []]]; +}; + +Constraints.Angle.prototype.serialize = function() { + return [this.NAME, [this.p1.id, this.p2.id, this.p3.id, this.p4.id, this.angle]]; +}; + +Constraints.Factory[Constraints.Angle.prototype.NAME] = function(refs, data) { + return new Constraints.Angle( refs(data[0]), refs(data[1]), refs(data[2]), refs(data[3]), data[4] ); +}; + +Constraints.Angle.prototype.getObjects = function() { + var collector = new Constraints.ParentsCollector(); + collector.check(this.p1); + collector.check(this.p2); + collector.check(this.p3); + collector.check(this.p4); + return collector.parents; +}; + +Constraints.Angle.prototype.SettableFields = {'angle' : "Enter the angle value"}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.LockConvex = function(c, a, t) { + this.c = c; + this.a = a; + this.t = t; +}; + +Constraints.LockConvex.prototype.NAME = 'LockConvex'; +Constraints.LockConvex.prototype.UI_NAME = 'Lock Convexity'; + +Constraints.LockConvex.prototype.getSolveData = function() { + var params = []; + this.c.collectParams(params); + this.a.collectParams(params); + this.t.collectParams(params); + return [['LockConvex', params, []]]; +}; + +Constraints.LockConvex.prototype.serialize = function() { + return [this.NAME, [this.c.id, this.a.id, this.t.id]]; +}; + +Constraints.Factory[Constraints.LockConvex.prototype.NAME] = function(refs, data) { + return new Constraints.LockConvex(refs(data[0]), refs(data[1]), refs(data[2])); +}; + +Constraints.LockConvex.prototype.getObjects = function() { + var collector = new Constraints.ParentsCollector(); + collector.check(this.c); + collector.check(this.a); + collector.check(this.t); + return collector.parents; +}; + + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Mirror = function(reflectionLine, objects, reflectedObjects) { + this.reflectionLine = reflectionLine; + this.objects = objects; + this.dir = new Vector(); + this.updateDir(); + if (!reflectedObjects) { + reflectedObjects = objects.map(o => { + let copy = o.copy(); + copy.virtualOf = o.id; + copy.aux = true; + copy.role = 'virtual'; + o.layer.add(copy); + this.reflect(o, copy); + return copy; + }); + } + reflectedObjects.forEach((copy, i) => { + copy.virtualOf = this.objects[i].id; + copy.aux = true; + copy.role = 'virtual'; + }); + this.reflectedObjects = reflectedObjects; +}; + +Constraints.Mirror.prototype.NAME = 'Mirror'; +Constraints.Mirror.prototype.UI_NAME = 'Mirror'; +Constraints.Mirror.prototype.GENERATOR = true; + +Constraints.Mirror.prototype.updateDir = function() { + this.dir.set(-(this.reflectionLine.b._y.get() - this.reflectionLine.a._y.get()), this.reflectionLine.b._x.get() - this.reflectionLine.a._x.get(), 0)._normalize(); +}; + +Constraints.Mirror.prototype.reflect = function(source, dest) { + let origin = this.reflectionLine.a.toVector(); + + const pointMirroring = (x, y) => { + let pt = new Vector(x, y, 0); + let proj = this.dir.dot(pt.minus(origin)); + return this.dir.multiply(- proj * 2)._plus(pt); + }; + + source.mirror(dest, pointMirroring); +}; + +Constraints.Mirror.prototype.getSolveData = function() { + return []; +}; + +Constraints.Mirror.prototype.serialize = function() { + let ids = [this.reflectionLine.id]; + for (let i = 0; i < this.objects.length; i++) { + ids.push(this.objects[i].id); + ids.push(this.reflectedObjects[i].id); + } + return [this.NAME, ids]; +}; + +Constraints.Factory[Constraints.Mirror.prototype.NAME] = function(refs, data) { + let [rlId, ...objectIds] = data; + let objects = []; + let reflectedObjects = []; + for (let i = 0; i < objectIds.length; i += 2) { + objects.push(refs(objectIds[i])); + reflectedObjects.push(refs(objectIds[i + 1])); + } + return new Constraints.Mirror(refs(rlId), objects, reflectedObjects); +}; + +Constraints.Mirror.prototype.getObjects = function() { + return [this.reflectionLine, ...this.objects]; +}; + +Constraints.Mirror.prototype.visitParams = function(callback) { + this.reflectionLine.visitParams(callback); + this.objects.forEach(o => o.visitParams(callback)); +}; + +Constraints.Mirror.prototype.visitGeneratedParams = function(callback) { + this.reflectedObjects.forEach(o => o.visitParams(callback)); +}; + + +Constraints.Mirror.prototype.updateGeneratedObjects = function() { + this.updateDir(); + for (let i = 0; i < this.objects.length; i++) { + this.reflect(this.objects[i], this.reflectedObjects[i]); + } +}; + +Constraints.Mirror.prototype.getGeneratedObjects = function() { + return this.reflectedObjects; +}; + +// ------------------------------------------------------------------------------------------------------------------ // + +/** @constructor */ +Constraints.Fillet = function(point1, point2, arc) { + this.point1 = point1; + this.point2 = point2; + this.arc = arc; + this.contraints = [ + new Constraints.Tangent( arc, point1.parent), + new Constraints.Tangent( arc, point2.parent), + new Constraints.Coincident( arc.a, point1), + new Constraints.Coincident( arc.b, point2) + ]; +}; + +Constraints.Fillet.prototype.NAME = 'Fillet'; +Constraints.Fillet.prototype.UI_NAME = 'Fillet'; + +Constraints.Fillet.prototype.getSolveData = function() { + let solveData = []; + this.contraints.forEach(c => c.getSolveData().forEach(d => solveData.push(d))); + return solveData; +}; + +Constraints.Fillet.prototype.serialize = function() { + return [this.NAME, [this.point1.id, this.point2.id, this.arc.id]]; +}; + +Constraints.Factory[Constraints.Fillet.prototype.NAME] = function(refs, data) { + return new Constraints.Fillet(refs(data[0]), refs(data[1]), refs(data[2])); +}; + +Constraints.Fillet.prototype.getObjects = function() { + let objects = []; + this.contraints.forEach(c => c.getObjects().forEach(o => objects.push(o))); + return objects; +}; + + +Constraints.Fillet.prototype.validate = function() { + + function validOn(p, arc, left) { + let op = p.parent.opposite(p); + let opV = op.toVector(); + let dir = p.toVector()._minus(opV)._normalize(); + let centerDir = arc.c.toVector()._minus(opV)._normalize(); + let z = centerDir.cross(dir).z; + + return left === z < 0; + } + return validOn(this.point1, this.arc, true) && validOn(this.point2, this.arc, false); +}; + + +Constraints.ParentsCollector = function() { + this.parents = []; + var parents = this.parents; + var index = {}; + function add(obj) { + if (index[obj.id] === undefined) { + index[obj.id] = obj; + parents.push(obj); + } + } + this.check = function(obj) { + if (obj.parent !== null) { + add(obj.parent); + } else { + add(obj); + } + }; +}; + diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index f128dfd3..a18cc5e4 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -15,6 +15,7 @@ import Vector from 'math/vector'; import exportTextData from 'gems/exportTextData'; import NurbsCurve from '../brep/geom/curves/nurbsCurve'; import {NurbsObject} from './shapes/nurbsObject'; +import {System} from './system'; const Types = { END_POINT : 'TCAD.TWO.EndPoint', @@ -319,8 +320,8 @@ IO.prototype.cleanUpData = function() { } this.viewer.deselectAll(); Generator.resetIDGenerator(0); - if (this.viewer.parametricManager.subSystems.length != 0) { - this.viewer.parametricManager.subSystems = []; + if (this.viewer.parametricManager.system.subSystems.length !== 0) { + this.viewer.parametricManager.system = new System(); this.viewer.parametricManager.notify(); } }; @@ -386,7 +387,7 @@ IO.prototype._serializeSketch = function() { } var constrs = sketch['constraints'] = []; - var subSystems = this.viewer.parametricManager.subSystems; + var subSystems = this.viewer.parametricManager.system.subSystems; for (var j = 0; j < subSystems.length; j++) { var sub = subSystems[j]; for (i = 0; i < sub.constraints.length; ++i) { diff --git a/web/app/sketcher/mirror.js b/web/app/sketcher/mirror.js new file mode 100644 index 00000000..751cf2f1 --- /dev/null +++ b/web/app/sketcher/mirror.js @@ -0,0 +1,16 @@ + + +function mirrorManager() { + + let mirrors = []; + + function add() { + + } + + +} + +function mirrorTool() { + +} \ No newline at end of file diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index c37ea080..0b65327d 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -1,31 +1,31 @@ -import * as utils from '../utils/utils' -import {Ref} from './shapes/ref' -import {Param, prepare} from './constr/solver' -import {createByConstraintName} from './constr/constraints' +import * as utils from '../utils/utils'; +import {Ref} from './shapes/ref'; +import {Param, prepare} from './constr/solver'; +import {createByConstraintName} from './constr/solverConstraints'; import Vector from 'math/vector'; -import * as math from '../math/math' -import * as fetch from './fetchers' - -var Constraints = {}; +import * as math from '../math/math'; +import * as fetch from './fetchers'; +import {Types} from './io'; +import {pointIterator} from './shapes/sketch-object'; +import {buildSystem, System} from './system'; /** @constructor */ -function SubSystem() { - this.alg = 1; - this.error = 0; - this.reduce = false; - this.constraints = []; -} +class ParametricManager { + constructor(viewer) { + this.viewer = viewer; + this.system = new System(); + this.listeners = []; + this.constantTable = {}; -/** @constructor */ -function ParametricManager(viewer) { - this.viewer = viewer; - this.subSystems = []; - this.listeners = []; - this.constantTable = {}; + this.viewer.params.define('constantDefinition', null); + this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)(); + this.constantResolver = this.createConstantResolver(); + this.messageSink = msg => alert(msg); + } - this.viewer.params.define('constantDefinition', null); - this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)(); - this.constantResolver = this.createConstantResolver(); + get subSystems() { + return this.system.subSystems; + } } ParametricManager.prototype.createConstantResolver = function() { @@ -87,46 +87,18 @@ ParametricManager.prototype.defineNewConstant = function(name, value) { this.viewer.params.set('constantDefinition', constantDefinition, 'parametricManager'); }; -ParametricManager.prototype.findComponents = function(constr) { - if (this.subSystems.length === 0) { - this.subSystems.push(new SubSystem()); - } - return [0]; -}; - ParametricManager.prototype.tune = function(subSystem) { }; ParametricManager.prototype._add = function(constr) { - var subSystemIds = this.findComponents(constr); - var subSystem; - switch (subSystemIds.length) { - case 0: - subSystem = new 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); - return subSystem; + this.system.add(constr); }; -ParametricManager.prototype.checkRedundancy = function (subSystem, constr) { - var solver = this.prepareForSubSystem([], subSystem.constraints); - if (solver.diagnose().conflict) { - alert("Most likely this "+constr.NAME + " constraint is CONFLICTING!") +ParametricManager.prototype.checkRedundancy = function (subSystem, causingSubject) { + const solver = this.prepare([]); + if (solver.hasConflicts()) { + this.messageSink("Most likely this " + causingSubject + " constraint is CONFLICTING!"); } }; @@ -138,72 +110,83 @@ ParametricManager.prototype.refresh = function() { ParametricManager.prototype.add = function(constr) { this.viewer.historyManager.checkpoint(); - var subSystem = this._add(constr); - this.checkRedundancy(subSystem, constr); + this._add(constr); + this.checkRedundancy(constr.NAME); this.refresh(); }; ParametricManager.prototype.addAll = function(constrs) { - for (var i = 0; i < constrs.length; i++) { - var subSystem = this._add(constrs[i]); - this.checkRedundancy(subSystem, constrs[i]); + for (let i = 0; i < constrs.length; i++) { + let subSystem = this._add(constrs[i]); + this.checkRedundancy(constrs[i].NAME); } this.refresh(); }; +ParametricManager.prototype.cleanUpOnRemove = function(constr) { + if (constr.NAME === 'coi') { + this.unlinkObjects(constr.a, constr.b); + } +}; + ParametricManager.prototype.remove = function(constr) { this.viewer.historyManager.checkpoint(); - 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.splice(i, 1); - if (p.NAME === 'coi') { - this.unlinkObjects(p.a, p.b); - } - break; - } - } + this.cleanUpOnRemove(constr); + this.system.remove(constr); + if (constr.GENERATOR) { + this.removeObjects(constr.getGeneratedObjects()); } this.refresh(); }; -ParametricManager.prototype.removeConstraintsByObj = function(obj) { - var ownedParams = []; - obj.collectParams(ownedParams); - this.removeConstraintsByParams(ownedParams); -}; +ParametricManager.prototype.removeObjects = function(objects) { -ParametricManager.prototype.removeConstraintsByParams = function(ownedParams) { - for (var s = 0; s < this.subSystems.length; s++) { - var toRemove = []; - var sub = this.subSystems[s]; - var i; - for (i = 0; i < sub.constraints.length; ++i) { - var sdataArr = sub.constraints[i].getSolveData(this.constantResolver); - MAIN: - for (var j = 0; j < sdataArr.length; j++) { - var sdata = sdataArr[j]; - var params = sdata[1]; - 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; - } - } + let toRemove = new Set(); + const dependants = []; + + objects.forEach(obj => obj.visitParams(p => { + let constraints = this.system.paramToConstraintsIndex.get(p); + if (constraints) { + constraints.forEach(constr => { + if (constr.GENERATOR) { + constr.getGeneratedObjects().forEach(o => dependants.push(o)); } - } + }); } - toRemove.sort(); - - for (i = toRemove.length - 1; i >= 0 ; --i) { - sub.constraints.splice( toRemove[i], 1); - } - } + })); - this.notify(); + + objects.forEach(obj => obj.visitParams(p => { + let constraints = this.system.paramToConstraintsIndex.get(p); + if (constraints) { + constraints.forEach(constr => { + toRemove.add(constr); + }); + } + })); + + objects.forEach(obj => { + 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); + } }; ParametricManager.prototype.lock = function(objs) { @@ -424,6 +407,22 @@ ParametricManager.prototype.radius = function(objs, promptCallback) { } }; +ParametricManager.prototype.mirror = function(objs, promptCallback) { + let reflectionLine = null; + let source = []; + objs.forEach(o => { + if (o._class === Types.SEGMENT && reflectionLine === null) { + reflectionLine = o; + } else { + source.push(o); + } + }); + if (reflectionLine === null || source.length === 0) { + throw "Illegal Argument. Constraint requires at least 1 line and 1 object"; + } + this.add(new Constraints.Mirror(reflectionLine, source)); +}; + ParametricManager.prototype._linkObjects = function(objs) { var i; var masterIdx = -1; @@ -510,53 +509,76 @@ ParametricManager.prototype.__getSolveData = function(constraints, out) { ParametricManager.prototype.solve = function(lock, extraConstraints, disabledObjects) { const solver = this.prepare(lock, extraConstraints, disabledObjects); solver.solve(false); - solver.sync(); }; ParametricManager.prototype.prepare = function(locked, extraConstraints, disabledObjects) { - return this._prepare(locked, this.subSystems, extraConstraints, disabledObjects); + const orderedSubsystems = []; + this.system.traverse(s => orderedSubsystems.push(s), () => console.error("Circular mirror constraints. System constrained incorrectly")); + return this._prepare(locked, orderedSubsystems, extraConstraints, disabledObjects); }; ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints, disabledObjects) { - var solvers = []; - for (var i = 0; i < subSystems.length; i++) { + const solvers = []; + for (let i = 0; i < subSystems.length; i++) { solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints, disabledObjects)); } - if (subSystems.length == 0 && locked && locked.length != 0) { + if (subSystems.length === 0 && locked && locked.length !== 0) { solvers.push(this.prepareForSubSystem(locked, [], extraConstraints, disabledObjects)); } + const viewer = this.viewer; return { solvers : solvers, solve : function(rough) { - for (var i = 0; i < solvers.length; i++) { - var alg = i < subSystems.length ? subSystems[i].alg : 1; - var res = solvers[i].solve(rough, alg); + for (let i = 0; i < solvers.length; i++) { + let alg = i < subSystems.length ? subSystems[i].alg : 1; + let res = solvers[i].solve(rough, alg); if (res.returnCode !== 1) { - alg = alg == 1 ? 2 : 1; + alg = alg === 1 ? 2 : 1; //if (solvers[i].solve(rough, alg).returnCode == 1) { //subSystems[i].alg = alg; //} } + + for (let i = 0; i < solvers.length; i++) { + solvers[i].sync(); + } + + subSystems.forEach(subSystem => { + subSystem.constraints.forEach(constr => { + if (constr.GENERATOR) { + constr.updateGeneratedObjects(); + constr.getGeneratedObjects().forEach(o => o.visitParams(p => { + for (let i = 0; i < solvers.length; i++) { + solvers[i].updateParameter(p); + } + })) + } + }) + }); } + viewer.equalizeLinkedEndpoints(); }, - sync : function() { - for (var i = 0; i < solvers.length; i++) { - solvers[i].sync(); - } - }, - updateParameter : function(p) { - for (var i = 0; i < solvers.length; i++) { + for (let i = 0; i < solvers.length; i++) { solvers[i].updateParameter(p); } }, updateLock : function(values) { - for (var i = 0; i < solvers.length; i++) { + for (let i = 0; i < solvers.length; i++) { solvers[i].updateLock(values); } + }, + + hasConflicts: function() { + for (let i = 0; i < solvers.length; i++) { + if (solvers[i].diagnose().conflict) { + return true + } + } + return false; } } }; @@ -760,13 +782,13 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons var readOnlyParams = auxParams.concat(locked); var reduceInfo = ParametricManager.reduceSystem(system, readOnlyParams); - function getSolverParam(p) { + function getSolverParam(p, doNotCreate = false) { var master = reduceInfo.reducedParams[p.id]; if (master !== undefined) { p = reduceInfo.idToParam[master]; } var _p = solverParamsDict[p.id]; - if (_p === undefined) { + if (_p === undefined && !doNotCreate) { if (p.__cachedParam__ === undefined) { _p = new Param(p.id, p.get()); p.__cachedParam__ = _p; @@ -839,11 +861,13 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons slave.set(master.get()); } } - viewer.equalizeLinkedEndpoints(); } - function updateParameter(p) { - getSolverParam(p).set(p.get()); + function updateParameter(p, force = false) { + let solverParam = getSolverParam(p); + if (solverParam) { + solverParam.set(p.get(), force); + } } solver.solve = solve; @@ -852,926 +876,5 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons return solver; }; -Constraints.ParentsCollector = function() { - this.parents = []; - var parents = this.parents; - var index = {}; - function add(obj) { - if (index[obj.id] === undefined) { - index[obj.id] = obj; - parents.push(obj); - } - } - this.check = function(obj) { - if (obj.parent !== null) { - add(obj.parent); - } else { - add(obj); - } - }; -}; - -Constraints.Factory = {}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Coincident = function(a, b) { - this.a = a; - this.b = b; - a.linked.push(b); - b.linked.push(a); -}; - -Constraints.Coincident.prototype.NAME = 'coi'; -Constraints.Coincident.prototype.UI_NAME = 'Coincident'; -Constraints.Coincident.prototype.reducible = true; - -Constraints.Coincident.prototype.getSolveData = function() { - return [ - ['equal', [this.a._x, this.b._x], []], - ['equal', [this.a._y, this.b._y], []] - ]; -}; - -Constraints.Coincident.prototype.serialize = function() { - return [this.NAME, [this.a.id, this.b.id]]; -}; - -Constraints.Factory[Constraints.Coincident.prototype.NAME] = function(refs, data) { - return new Constraints.Coincident(refs(data[0]), refs(data[1])); -}; - -Constraints.Coincident.prototype.getObjects = function() { - return [this.a, this.b]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.RadiusOffset = function(arc1, arc2, offset) { - this.arc1 = arc1; - this.arc2 = arc2; - this.offset = offset; -}; - -Constraints.RadiusOffset.prototype.NAME = 'RadiusOffset'; -Constraints.RadiusOffset.prototype.UI_NAME = 'Radius Offset'; - -Constraints.RadiusOffset.prototype.getSolveData = function(resolver) { - return [ - ['Diff', [this.arc1.r, this.arc2.r], [resolver(this.offset)]] - ]; -}; - -Constraints.RadiusOffset.prototype.serialize = function() { - return [this.NAME, [this.arc1.id, this.arc2.id, this.offset]]; -}; - -Constraints.Factory[Constraints.RadiusOffset.prototype.NAME] = function(refs, data) { - return new Constraints.RadiusOffset(refs(data[0]), refs(data[1]), data[2]); -}; - -Constraints.RadiusOffset.prototype.getObjects = function() { - return [this.arc1, this.arc2]; -}; - -Constraints.RadiusOffset.prototype.SettableFields = {'offset' : "Enter the offset"}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Lock = function(p, c) { - this.p = p; - this.c = c; -}; - -Constraints.Lock.prototype.NAME = 'lock'; -Constraints.Lock.prototype.UI_NAME = 'Lock'; - -Constraints.Lock.prototype.getSolveData = function() { - return [ - ['equalsTo', [this.p._x], [this.c.x]], - ['equalsTo', [this.p._y], [this.c.y]] - ]; -}; - -Constraints.Lock.prototype.serialize = function() { - return [this.NAME, [this.p.id, this.c]]; -}; - -Constraints.Factory[Constraints.Lock.prototype.NAME] = function(refs, data) { - return new Constraints.Lock(refs(data[0]), data[1]); -}; - - -Constraints.Lock.prototype.getObjects = function() { - return [this.p]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Parallel = function(l1, l2) { - this.l1 = l1; - this.l2 = l2; -}; - -Constraints.Parallel.prototype.NAME = 'parallel'; -Constraints.Parallel.prototype.UI_NAME = 'Parallel'; - -Constraints.Parallel.prototype.getSolveData = function() { - var params = []; - this.l1.collectParams(params); - this.l2.collectParams(params); - return [[this.NAME, params, []]]; -}; - -Constraints.Parallel.prototype.serialize = function() { - return [this.NAME, [this.l1.id, this.l2.id]]; -}; - -Constraints.Factory[Constraints.Parallel.prototype.NAME] = function(refs, data) { - return new Constraints.Parallel(refs(data[0]), refs(data[1])); -}; - -Constraints.Parallel.prototype.getObjects = function() { - return [this.l1, this.l2]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Perpendicular = function(l1, l2) { - this.l1 = l1; - this.l2 = l2; -}; - -Constraints.Perpendicular.prototype.NAME = 'perpendicular'; -Constraints.Perpendicular.prototype.UI_NAME = 'Perpendicular'; - -Constraints.Perpendicular.prototype.getSolveData = function() { - var params = []; - this.l1.collectParams(params); - this.l2.collectParams(params); - return [[this.NAME, params, []]]; -}; - -Constraints.Perpendicular.prototype.serialize = function() { - return [this.NAME, [this.l1.id, this.l2.id]]; -}; - -Constraints.Factory[Constraints.Perpendicular.prototype.NAME] = function(refs, data) { - return new Constraints.Perpendicular(refs(data[0]), refs(data[1])); -}; - -Constraints.Perpendicular.prototype.getObjects = function() { - return [this.l1, this.l2]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @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; - this.l = l; - this.d = d; -}; - -Constraints.P2LDistance.prototype.NAME = 'P2LDistance'; -Constraints.P2LDistance.prototype.UI_NAME = 'Distance P & L'; - -Constraints.P2LDistance.prototype.getSolveData = function(resolver) { - var params = []; - this.p.collectParams(params); - this.l.collectParams(params); - return [[this.NAME, params, [resolver(this.d)]]]; -}; - -Constraints.P2LDistance.prototype.serialize = function() { - return [this.NAME, [this.p.id, this.l.id, this.d]]; -}; - -Constraints.Factory[Constraints.P2LDistance.prototype.NAME] = function(refs, data) { - return new Constraints.P2LDistance(refs(data[0]), refs(data[1]), data[2]); -}; - -Constraints.P2LDistance.prototype.getObjects = function() { - return [this.p, this.l]; -}; - -Constraints.P2LDistance.prototype.SettableFields = {'d' : "Enter the distance"}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.MinLength = function(a, b, min) { - this.a = a; - this.b = b; - this.min = min; -}; - -Constraints.MinLength.prototype.aux = true; -Constraints.MinLength.prototype.NAME = 'MinLength'; -Constraints.MinLength.prototype.UI_NAME = 'MinLength'; - -Constraints.MinLength.prototype.getSolveData = function() { - var params = []; - this.a.collectParams(params); - this.b.collectParams(params); - return [[this.NAME, params, [this.min]]]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.P2LDistanceV = function(p, l, d) { - this.p = p; - this.l = l; - this.d = d; -}; - -Constraints.P2LDistanceV.prototype.aux = true; -Constraints.P2LDistanceV.prototype.NAME = 'P2LDistanceV'; -Constraints.P2LDistanceV.prototype.UI_NAME = 'Distance P & L'; - -Constraints.P2LDistanceV.prototype.getSolveData = function() { - var params = []; - this.p.collectParams(params); - this.l.collectParams(params); - params.push(this.d); - return [[this.NAME, params]]; -}; - -// We don't serialize auxiliary constraints -// -//Constraints.P2LDistanceV.prototype.serialize = function() { -// return [this.NAME, [this.p.id, this.l.id, this.d.id]]; -//}; -// -//Constraints.Factory[Constraints.P2LDistanceV.prototype.NAME] = function(refs, data) { -// return new Constraints.P2LDistanceV(refs(data[0]), refs(data[1]), refs(data[2])); -//}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.P2PDistance = function(p1, p2, d) { - this.p1 = p1; - this.p2 = p2; - this.d = d; -}; - -Constraints.P2PDistance.prototype.NAME = 'P2PDistance'; -Constraints.P2PDistance.prototype.UI_NAME = 'Distance Points'; - -Constraints.P2PDistance.prototype.getSolveData = function(resolver) { - var params = []; - this.p1.collectParams(params); - this.p2.collectParams(params); - return [[this.NAME, params, [resolver(this.d)]]]; -}; - -Constraints.P2PDistance.prototype.serialize = function() { - return [this.NAME, [this.p1.id, this.p2.id, this.d]]; -}; - -Constraints.Factory[Constraints.P2PDistance.prototype.NAME] = function(refs, data) { - return new Constraints.P2PDistance(refs(data[0]), refs(data[1]), data[2]); -}; - -Constraints.P2PDistance.prototype.getObjects = function() { - return [this.p1, this.p2]; -}; - -Constraints.P2PDistance.prototype.SettableFields = {'d' : "Enter the distance"}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.P2PDistanceV = function(p1, p2, d) { - this.p1 = p1; - this.p2 = p2; - this.d = d; -}; - -Constraints.P2PDistanceV.prototype.aux = true; -Constraints.P2PDistanceV.prototype.NAME = 'P2PDistanceV'; -Constraints.P2PDistanceV.prototype.UI_NAME = 'Distance Points'; - -Constraints.P2PDistanceV.prototype.getSolveData = function() { - var params = []; - this.p1.collectParams(params); - this.p2.collectParams(params); - params.push(this.d); - return [[this.NAME, params]]; -}; - -// We don't serialize auxiliary constraints -// -//Constraints.P2PDistanceV.prototype.serialize = function() { -// return [this.NAME, [this.p1.id, this.p2.id, this.d.id]]; -//}; -// -//Constraints.Factory[Constraints.P2PDistanceV.prototype.NAME] = function(refs, data) { -// return new Constraints.P2PDistanceV(refs(data[0]), refs(data[1]), refs(data[2])); -//}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.GreaterThan = function(p, limit) { - this.p = p; - this.limit = limit; -}; - -Constraints.GreaterThan.prototype.aux = true; -Constraints.GreaterThan.prototype.NAME = 'GreaterThan'; -Constraints.GreaterThan.prototype.UI_NAME = 'Greater Than'; - -Constraints.GreaterThan.prototype.getSolveData = function() { - return [[this.NAME, [this.p], [this.limit]]]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Radius = function(arc, d) { - this.arc = arc; - this.d = d; -}; - -Constraints.Radius.prototype.NAME = 'Radius'; -Constraints.Radius.prototype.UI_NAME = 'Radius Value'; - - -Constraints.Radius.prototype.getSolveData = function(resolver) { - return [['equalsTo', [this.arc.r], [resolver(this.d)]]]; -}; - -Constraints.Radius.prototype.serialize = function() { - return [this.NAME, [this.arc.id, this.d]]; -}; - -Constraints.Factory[Constraints.Radius.prototype.NAME] = function(refs, data) { - return new Constraints.Radius(refs(data[0]), data[1]); -}; - -Constraints.Radius.prototype.getObjects = function() { - return [this.arc]; -}; - -Constraints.Radius.prototype.SettableFields = {'d' : "Enter the radius value"}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.RR = function(arc1, arc2) { - this.arc1 = arc1; - this.arc2 = arc2; -}; - -Constraints.RR.prototype.NAME = 'RR'; -Constraints.RR.prototype.UI_NAME = 'Radius Equality'; -//Constraints.RR.prototype.reducible = true; - - -Constraints.RR.prototype.getSolveData = function() { - return [['equal', [this.arc1.r, this.arc2.r], []]]; -}; - -Constraints.RR.prototype.serialize = function() { - return [this.NAME, [this.arc1.id, this.arc2.id]]; -}; - -Constraints.Factory[Constraints.RR.prototype.NAME] = function(refs, data) { - return new Constraints.RR(refs(data[0]), refs(data[1])); -}; - -Constraints.RR.prototype.getObjects = function() { - return [this.arc1, this.arc2]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.LL = function(line1, line2) { - this.line1 = line1; - this.line2 = line2; - this.length = new Ref(math.distanceAB(line1.a, line1.b)); -}; - -Constraints.LL.prototype.NAME = 'LL'; -Constraints.LL.prototype.UI_NAME = 'Lines Equality'; - -Constraints.LL.prototype.getSolveData = function() { - var params1 = []; - var params2 = []; - this.line1.collectParams(params1); - this.line2.collectParams(params2); - params1.push(this.length); - params2.push(this.length); - return [ - ['P2PDistanceV', params1, []], - ['P2PDistanceV', params2, []] - ]; -}; - -Constraints.LL.prototype.serialize = function() { - return [this.NAME, [this.line1.id, this.line2.id]]; -}; - -Constraints.Factory[Constraints.LL.prototype.NAME] = function(refs, data) { - return new Constraints.LL(refs(data[0]), refs(data[1])); -}; - -Constraints.LL.prototype.getObjects = function() { - return [this.line1, this.line2]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Vertical = function(line) { - this.line = line; -}; - -Constraints.Vertical.prototype.NAME = 'Vertical'; -Constraints.Vertical.prototype.UI_NAME = 'Vertical'; -//Constraints.Vertical.prototype.reducible = true; - -Constraints.Vertical.prototype.getSolveData = function() { - return [['equal', [this.line.a._x, this.line.b._x], []]]; -}; - -Constraints.Vertical.prototype.serialize = function() { - return [this.NAME, [this.line.id]]; -}; - -Constraints.Factory[Constraints.Vertical.prototype.NAME] = function(refs, data) { - return new Constraints.Vertical(refs(data[0])); -}; - -Constraints.Vertical.prototype.getObjects = function() { - return [this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Horizontal = function(line) { - this.line = line; -}; - -Constraints.Horizontal.prototype.NAME = 'Horizontal'; -Constraints.Horizontal.prototype.UI_NAME = 'Horizontal'; -//Constraints.Horizontal.prototype.reducible = true; - -Constraints.Horizontal.prototype.getSolveData = function() { - return [['equal', [this.line.a._y, this.line.b._y], []]]; -}; - -Constraints.Horizontal.prototype.serialize = function() { - return [this.NAME, [this.line.id]]; -}; - -Constraints.Factory[Constraints.Horizontal.prototype.NAME] = function(refs, data) { - return new Constraints.Horizontal(refs(data[0])); -}; - -Constraints.Horizontal.prototype.getObjects = function() { - return [this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Tangent = function(arc, line) { - this.arc = arc; - this.line = line; -}; - -Constraints.Tangent.prototype.NAME = 'Tangent'; -Constraints.Tangent.prototype.UI_NAME = 'Tangent'; - -Constraints.Tangent.prototype.getSolveData = function() { - var params = []; - this.arc.c.collectParams(params); - this.line.collectParams(params); - params.push(this.arc.r); - return [['P2LDistanceV', params, []]]; -}; - -Constraints.Tangent.prototype.serialize = function() { - return [this.NAME, [this.arc.id, this.line.id]]; -}; - -Constraints.Factory[Constraints.Tangent.prototype.NAME] = function(refs, data) { - return new Constraints.Tangent(refs(data[0]), refs(data[1])); -}; - -Constraints.Tangent.prototype.getObjects = function() { - return [this.arc, this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointOnLine = function(point, line) { - this.point = point; - this.line = line; -}; - -Constraints.PointOnLine.prototype.NAME = 'PointOnLine'; -Constraints.PointOnLine.prototype.UI_NAME = 'Point On Line'; - -Constraints.PointOnLine.prototype.getSolveData = function() { - var params = []; - this.point.collectParams(params); - this.line.collectParams(params); - return [['P2LDistance', params, [0]]]; -}; - -Constraints.PointOnLine.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.line.id]]; -}; - -Constraints.Factory[Constraints.PointOnLine.prototype.NAME] = function(refs, data) { - return new Constraints.PointOnLine(refs(data[0]), refs(data[1])); -}; - -Constraints.PointOnLine.prototype.getObjects = function() { - return [this.point, this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointOnArc = function(point, arc) { - this.point = point; - this.arc = arc; -}; - -Constraints.PointOnArc.prototype.NAME = 'PointOnArc'; -Constraints.PointOnArc.prototype.UI_NAME = 'Point On Arc'; - -Constraints.PointOnArc.prototype.getSolveData = function() { - var params = []; - this.point.collectParams(params); - this.arc.c.collectParams(params); - params.push(this.arc.r); - return [['P2PDistanceV', params, []]]; -}; - -Constraints.PointOnArc.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.arc.id]]; -}; - -Constraints.Factory[Constraints.PointOnArc.prototype.NAME] = function(refs, data) { - return new Constraints.PointOnArc(refs(data[0]), refs(data[1])); -}; - -Constraints.PointOnArc.prototype.getObjects = function() { - return [this.point, this.arc]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointOnCurve = function(point, curveObject) { - this.point = point; - this.curveObject = curveObject; -}; - -Constraints.PointOnCurve.prototype.NAME = 'PointOnCurve'; -Constraints.PointOnCurve.prototype.UI_NAME = 'Point On Curve'; - -Constraints.PointOnCurve.prototype.getSolveData = function() { - const params = []; - this.point.collectParams(params); - return [['PointOnCurve', params, [this.curveObject.curve]]]; -}; - -Constraints.PointOnCurve.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.curveObject.id]]; -}; - -Constraints.Factory[Constraints.PointOnCurve.prototype.NAME] = function(refs, data) { - return new Constraints.PointOnCurve(refs(data[0]), refs(data[1])); -}; - -Constraints.PointOnCurve.prototype.getObjects = function() { - return [this.point, this.curveObject]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointOnEllipseInternal = function(point, ellipse) { - this.point = point; - this.ellipse= ellipse; -}; - -Constraints.PointOnEllipseInternal.prototype.NAME = 'PointOnEllipseI'; -Constraints.PointOnEllipseInternal.prototype.UI_NAME = 'Point On Ellipse'; -Constraints.PointOnEllipseInternal.prototype.aux = true; - -Constraints.PointOnEllipseInternal.prototype.getSolveData = function() { - var params = []; - this.point.collectParams(params); - this.ellipse.ep1.collectParams(params); - this.ellipse.ep2.collectParams(params); - params.push(this.ellipse.r); - return [['PointOnEllipse', params, []]]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointOnEllipse = function(point, ellipse) { - Constraints.PointOnEllipseInternal.call(this, point, ellipse); -}; - -Constraints.PointOnEllipse.prototype.NAME = 'PointOnEllipse'; -Constraints.PointOnEllipse.prototype.UI_NAME = 'Point On Ellipse'; - -Constraints.PointOnEllipse.prototype.getSolveData = function() { - return Constraints.PointOnEllipseInternal.prototype.getSolveData.call(this); -}; - -Constraints.PointOnEllipse.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.ellipse.id]]; -}; - -Constraints.Factory[Constraints.PointOnEllipse.prototype.NAME] = function(refs, data) { - return new Constraints.PointOnEllipse(refs(data[0]), refs(data[1])); -}; - -Constraints.PointOnEllipse.prototype.getObjects = function() { - return [this.point, this.ellipse]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.EllipseTangent = function(line, ellipse) { - this.line = line; - this.ellipse = ellipse; -}; - -Constraints.EllipseTangent.prototype.NAME = 'EllipseTangent'; -Constraints.EllipseTangent.prototype.UI_NAME = 'Tangent Ellipse'; - -Constraints.EllipseTangent.prototype.getSolveData = function() { - const params = []; - this.line.collectParams(params); - this.ellipse.ep1.collectParams(params); - this.ellipse.ep2.collectParams(params); - params.push(this.ellipse.r); - return [['EllipseTangent', params, []]]; - -}; - -Constraints.EllipseTangent.prototype.serialize = function() { - return [this.NAME, [this.line.id, this.ellipse.id]]; -}; - -Constraints.Factory[Constraints.EllipseTangent.prototype.NAME] = function(refs, data) { - return new Constraints.EllipseTangent(refs(data[0]), refs(data[1])); -}; - -Constraints.EllipseTangent.prototype.getObjects = function() { - return [this.line, this.ellipse]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.CurveTangent = function(line, curveObject) { - this.line = line; - this.curveObject = curveObject; - let [uMin, uMax] = this.curveObject.curve.domain(); - let initPoint = this.curveObject.curve.point(0.5 * (uMin + uMax)); - this.tx = new Ref(initPoint[0]); - this.ty = new Ref(initPoint[1]); -}; - -Constraints.CurveTangent.prototype.NAME = 'CurveTangent'; -Constraints.CurveTangent.prototype.UI_NAME = 'Curve Curve'; - -Constraints.CurveTangent.prototype.getSolveData = function() { - const params = []; - this.line.collectParams(params); - params.push(this.tx); - params.push(this.ty); - return [['CurveTangent', params, [this.curveObject.curve]]]; - -}; - -Constraints.CurveTangent.prototype.serialize = function() { - return [this.NAME, [this.line.id, this.curveObject.id]]; -}; - -Constraints.Factory[Constraints.CurveTangent.prototype.NAME] = function(refs, data) { - return new Constraints.CurveTangent(refs(data[0]), refs(data[1])); -}; - -Constraints.CurveTangent.prototype.getObjects = function() { - return [this.line, this.curveObject]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.PointInMiddle = function(point, line) { - this.point = point; - this.line = line; - this.length = new Ref(math.distanceAB(line.a, line.b) / 2); -}; - -Constraints.PointInMiddle.prototype.NAME = 'PointInMiddle'; -Constraints.PointInMiddle.prototype.UI_NAME = 'Point In the Middle'; - -Constraints.PointInMiddle.prototype.getSolveData = function() { - var params1 = []; - var params2 = []; - - this.line.a.collectParams(params1); - this.point.collectParams(params1); - params1.push(this.length); - - this.line.b.collectParams(params2); - this.point.collectParams(params2); - params2.push(this.length); - - return [ - ['P2PDistanceV', params1, []], - ['P2PDistanceV', params2, []] - ]; -}; - -Constraints.PointInMiddle.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.line.id]]; -}; - -Constraints.Factory[Constraints.PointInMiddle.prototype.NAME] = function(refs, data) { - return new Constraints.PointInMiddle(refs(data[0]), refs(data[1])); -}; - -Constraints.PointInMiddle.prototype.getObjects = function() { - return [this.point, this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Symmetry = function(point, line) { - this.point = point; - this.line = line; - this.length = new Ref(math.distanceAB(line.a, line.b) / 2); -}; - -Constraints.Symmetry.prototype.NAME = 'Symmetry'; -Constraints.Symmetry.prototype.UI_NAME = 'Symmetry'; - -Constraints.Symmetry.prototype.getSolveData = function(resolver) { - var pointInMiddleData = Constraints.PointInMiddle.prototype.getSolveData.call(this, [resolver]); - var pointOnLineData = Constraints.PointOnLine.prototype.getSolveData.call(this, [resolver]); - return pointInMiddleData.concat(pointOnLineData); -}; - -Constraints.Symmetry.prototype.serialize = function() { - return [this.NAME, [this.point.id, this.line.id]]; -}; - -Constraints.Factory[Constraints.Symmetry.prototype.NAME] = function(refs, data) { - return new Constraints.Symmetry(refs(data[0]), refs(data[1])); -}; - -Constraints.Symmetry.prototype.getObjects = function() { - return [this.point, this.line]; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.Angle = function(p1, p2, p3, p4, angle) { - this.p1 = p1; - this.p2 = p2; - this.p3 = p3; - this.p4 = p4; - this._angle = new Ref(0); - this.angle = angle; -}; - -Constraints.Angle.prototype.NAME = 'Angle'; -Constraints.Angle.prototype.UI_NAME = 'Lines Angle'; - -Constraints.Angle.prototype.getSolveData = function(resolver) { - this._angle.set(resolver(this.angle) / 180 * Math.PI); - var params = []; - this.p1.collectParams(params); - this.p2.collectParams(params); - this.p3.collectParams(params); - this.p4.collectParams(params); - params.push(this._angle); - return [['angleConst', params, []]]; -}; - -Constraints.Angle.prototype.serialize = function() { - return [this.NAME, [this.p1.id, this.p2.id, this.p3.id, this.p4.id, this.angle]]; -}; - -Constraints.Factory[Constraints.Angle.prototype.NAME] = function(refs, data) { - return new Constraints.Angle( refs(data[0]), refs(data[1]), refs(data[2]), refs(data[3]), data[4] ); -}; - -Constraints.Angle.prototype.getObjects = function() { - var collector = new Constraints.ParentsCollector(); - collector.check(this.p1); - collector.check(this.p2); - collector.check(this.p3); - collector.check(this.p4); - return collector.parents; -}; - -Constraints.Angle.prototype.SettableFields = {'angle' : "Enter the angle value"}; - -// ------------------------------------------------------------------------------------------------------------------ // - -/** @constructor */ -Constraints.LockConvex = function(c, a, t) { - this.c = c; - this.a = a; - this.t = t; -}; - -Constraints.LockConvex.prototype.NAME = 'LockConvex'; -Constraints.LockConvex.prototype.UI_NAME = 'Lock Convexity'; - -Constraints.LockConvex.prototype.getSolveData = function() { - var params = []; - this.c.collectParams(params); - this.a.collectParams(params); - this.t.collectParams(params); - return [['LockConvex', params, []]]; -}; - -Constraints.LockConvex.prototype.serialize = function() { - return [this.NAME, [this.c.id, this.a.id, this.t.id]]; -}; - -Constraints.Factory[Constraints.LockConvex.prototype.NAME] = function(refs, data) { - return new Constraints.LockConvex(refs(data[0]), refs(data[1]), refs(data[2])); -}; - -Constraints.LockConvex.prototype.getObjects = function() { - var collector = new Constraints.ParentsCollector(); - collector.check(this.c); - collector.check(this.a); - collector.check(this.t); - return collector.parents; -}; - -// ------------------------------------------------------------------------------------------------------------------ // - +import {Constraints} from './constraints'; export {Constraints, ParametricManager} \ No newline at end of file diff --git a/web/app/sketcher/shapes/arc.js b/web/app/sketcher/shapes/arc.js index 54bc46fe..c0febaca 100644 --- a/web/app/sketcher/shapes/arc.js +++ b/web/app/sketcher/shapes/arc.js @@ -3,7 +3,7 @@ import * as math from '../../math/math'; import Vector from 'math/vector'; import {Ref} from './ref' import {Constraints} from '../parametric' -import {SketchObject} from './sketch-object' +import {pointIterator, SketchObject} from './sketch-object'; export class Arc extends SketchObject { @@ -20,14 +20,14 @@ export class Arc extends SketchObject { this.r.value = this.distanceA(); this.r.obj = this; } - - collectParams(params) { - this.a.collectParams(params); - this.b.collectParams(params); - this.c.collectParams(params); - params.push(this.r); + + visitParams(callback) { + this.a.visitParams(callback); + this.b.visitParams(callback); + this.c.visitParams(callback); + callback(this.r); } - + getReferencePoint() { return this.c; } @@ -131,6 +131,12 @@ export class Arc extends SketchObject { copy() { return new Arc(this.a.copy(), this.b.copy(), this.c.copy()); } + + mirror(dest, mirroringFunc) { + this.a.mirror(dest.b, mirroringFunc); + this.b.mirror(dest.a, mirroringFunc); + this.c.mirror(dest.c, mirroringFunc); + } } Arc.prototype._class = 'TCAD.TWO.Arc'; diff --git a/web/app/sketcher/shapes/bezier-curve.js b/web/app/sketcher/shapes/bezier-curve.js index 6f286b8c..55ee1950 100644 --- a/web/app/sketcher/shapes/bezier-curve.js +++ b/web/app/sketcher/shapes/bezier-curve.js @@ -24,11 +24,11 @@ export class BezierCurve extends SketchObject { } } - collectParams(params) { - this.a.collectParams(params); - this.b.collectParams(params); - this.cp1.collectParams(params); - this.cp2.collectParams(params); + visitParams(callback) { + this.a.visitParams(callback); + this.b.visitParams(callback); + this.cp1.visitParams(callback); + this.cp2.visitParams(callback); } normalDistance(aim, scale) { diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index ccae1749..83e63db2 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -16,10 +16,10 @@ export class Circle extends SketchObject { this.r = new Ref(0); this.r.obj = this; } - - collectParams(params) { - this.c.collectParams(params); - params.push(this.r); + + visitParams(callback) { + this.c.visitParams(callback); + callback(this.r); } getReferencePoint() { diff --git a/web/app/sketcher/shapes/dim.js b/web/app/sketcher/shapes/dim.js index bd4f089a..bc8e9332 100644 --- a/web/app/sketcher/shapes/dim.js +++ b/web/app/sketcher/shapes/dim.js @@ -11,8 +11,8 @@ class LinearDimension extends SketchObject { this.b = b; this.flip = false; } - - collectParams(params) { + + visitParams(callback) { } getReferencePoint() { @@ -164,8 +164,8 @@ export class DiameterDimension extends SketchObject { this.obj = obj; this.angle = Math.PI / 4; } - - collectParams(params) { + + visitParams(callback) { } getReferencePoint() { diff --git a/web/app/sketcher/shapes/ellipse.js b/web/app/sketcher/shapes/ellipse.js index da980445..ccd0b78e 100644 --- a/web/app/sketcher/shapes/ellipse.js +++ b/web/app/sketcher/shapes/ellipse.js @@ -30,11 +30,11 @@ export class Ellipse extends SketchObject { } return recovered; } - - collectParams(params) { - this.ep1.collectParams(params); - this.ep2.collectParams(params); - params.push(this.r); + + visitParams(callback) { + this.ep1.visitParams(callback); + this.ep2.visitParams(callback); + callback(this.r); } diff --git a/web/app/sketcher/shapes/nurbsObject.js b/web/app/sketcher/shapes/nurbsObject.js index ec8ac3e9..f225e67f 100644 --- a/web/app/sketcher/shapes/nurbsObject.js +++ b/web/app/sketcher/shapes/nurbsObject.js @@ -19,9 +19,9 @@ export class NurbsObject extends SketchObject { this.bezierPieces = this.calcBezierPiecewise(); } - collectParams(params) { - this.a.collectParams(params); - this.b.collectParams(params); + visitParams(callback) { + this.a.visitParams(callback); + this.b.visitParams(callback); } normalDistance(aim) { diff --git a/web/app/sketcher/shapes/point.js b/web/app/sketcher/shapes/point.js index 73653e4a..95318940 100644 --- a/web/app/sketcher/shapes/point.js +++ b/web/app/sketcher/shapes/point.js @@ -13,25 +13,25 @@ export class EndPoint extends SketchObject { this._x = new Param(this, 'x'); this._y = new Param(this, 'y'); } - - collectParams(params) { - params.push(this._x); - params.push(this._y); + + visitParams(callback) { + callback(this._x); + callback(this._y); } - + normalDistance(aim) { return aim.minus(new Vector(this.x, this.y)).length(); } - + getReferencePoint() { return this; } - + translateImpl(dx, dy) { this.x += dx; this.y += dy; } - + drawImpl(ctx, scale) { DrawPoint(ctx, this.x, this.y, 3, scale) } @@ -48,14 +48,20 @@ export class EndPoint extends SketchObject { setFromArray(arr) { this.setXY(arr[0], arr[1]); } - + toVector() { return new Vector(this.x, this.y); } - + copy() { return new EndPoint(this.x, this.y); } + + mirror(dest, mirroringFunc) { + let {x, y} = mirroringFunc(this.x, this.y); + dest.x = x; + dest.y = y; + } } EndPoint.prototype._class = 'TCAD.TWO.EndPoint'; diff --git a/web/app/sketcher/shapes/segment.js b/web/app/sketcher/shapes/segment.js index f663f3c5..5ddc96cc 100644 --- a/web/app/sketcher/shapes/segment.js +++ b/web/app/sketcher/shapes/segment.js @@ -24,10 +24,10 @@ export class Segment extends SketchObject { return true; } } - - collectParams(params) { - this.a.collectParams(params); - this.b.collectParams(params); + + visitParams(callback) { + this.a.visitParams(callback); + this.b.visitParams(callback); } normalDistance(aim) { @@ -72,6 +72,16 @@ export class Segment extends SketchObject { // ctx.restore(); } + opposite(endPoint) { + if (endPoint === this.a) { + return this.b; + } else if (endPoint === this.b) { + return this.a; + } else { + return null; + } + } + copy() { return new Segment(this.a.copy(), this.b.copy()); } diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index c43dfc07..1a2c3f09 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -1,5 +1,6 @@ import {Generator} from '../id-generator' import {Shape} from './shape' +import {Types} from '../io'; export class SketchObject extends Shape { constructor() { @@ -89,5 +90,47 @@ export class SketchObject extends Shape { copy() { throw 'method not implemented'; } + + mirror(dest, mirroringFunc) { + + let sourcePoints = []; + + pointIterator(this, o => { + sourcePoints.push(o); + }); + + let i = 0; + pointIterator(dest, o => { + sourcePoints[i++].mirror(o, mirroringFunc); + }); + } + + visitParams(callback) { + throw 'method not implemented'; + } + + collectParams(params) { + this.visitParams(p => params.push(p)); + } + + get effectiveLayer() { + let shape = this; + while (shape) { + if (shape.layer) { + return shape.layer; + } + shape = shape.parent; + } + return null; + } +} + +export function pointIterator(shape, func) { + shape.accept(o => { + if (o._class === Types.END_POINT) { + func(o); + } + return true; + }); } diff --git a/web/app/sketcher/sketcher-app.js b/web/app/sketcher/sketcher-app.js index 554849a1..c7ac96c9 100644 --- a/web/app/sketcher/sketcher-app.js +++ b/web/app/sketcher/sketcher-app.js @@ -76,6 +76,10 @@ function App2D() { } checkForTerminalVisibility(); + this.registerAction('new', "Create New Sketch", function () { + app.newSketch(); + }); + this.registerAction('terminal', "Open/Close Terminal Window", function () { app.commandsWin.toggle(); checkForTerminalVisibility(); @@ -214,6 +218,10 @@ function App2D() { app.viewer.parametricManager.p2lDistance(app.viewer.selected, prompt); }); + this.registerAction('mirrorConstraint', "Mirror Constraint", function () { + app.viewer.parametricManager.mirror(app.viewer.selected); + }); + this.registerAction('P2PDistanceConstraint', "Distance Between two Points", function () { app.viewer.parametricManager.p2pDistance(app.viewer.selected, prompt); }); @@ -294,6 +302,10 @@ App2D.views = [ { name: 'Constraints', icon: 'cogs' + }, + { + name: 'Mirroring', + icon: 'mirror' } ]; diff --git a/web/app/sketcher/styles.js b/web/app/sketcher/styles.js index d6195fa4..8542ea9e 100644 --- a/web/app/sketcher/styles.js +++ b/web/app/sketcher/styles.js @@ -4,7 +4,11 @@ export const Styles = { strokeStyle : "#ffffff", fillStyle : "#000000" }, - + VIRTUAL: { + lineWidth : 2, + strokeStyle : "#ffffff88", + fillStyle : "#00000088" + }, SERVICE : { lineWidth : 0.3, strokeStyle : "#ff0000", diff --git a/web/app/sketcher/system.js b/web/app/sketcher/system.js new file mode 100644 index 00000000..a55ccd00 --- /dev/null +++ b/web/app/sketcher/system.js @@ -0,0 +1,290 @@ +import {addToSetInMap, removeFromSetInMap, removeInPlace} from '../../../modules/gems/iterables'; +import {ParametricManager} from './parametric'; + + +let SUB_SYSTEM_ORDER = 0; + +class SubSystem { + constructor() { + this.alg = 1; + this.error = 0; + this.reduce = false; + this.constraints = []; + this.dependencies = []; + this.nativeParams = new Set(); + + this._internaOrder = SUB_SYSTEM_ORDER++; + } + + mergeWith(other) { + other.constraints.forEach(c => this.constraints.push(c)); + other.dependencies.forEach(d => { + if (this.dependencies.indexOf(d) === -1) { + this.dependencies.push(d); + } + }); + other.nativeParams.forEach(p => this.nativeParams.add(p)); + } +} + +class Index { + + constructor() { + this.constraints = []; + this.paramToConstraintsIndex = new Map(); + this.paramToConstraintsGraph = new Map(); + this.generatorConstraints = []; + this.generatedParams = new Map(); + } + + _reset() { + this.constraints = []; + this.paramToConstraintsIndex.clear(); + this.paramToConstraintsGraph.clear(); + this.generatorConstraints = []; + this.generatedParams.clear(); + } + + _pushConstraint(constr) { + this.constraints.push(constr); + visitParams(constr, true, p => addToSetInMap(this.paramToConstraintsGraph, p, constr)); + visitParams(constr, false, p => addToSetInMap(this.paramToConstraintsIndex, p, constr)); + if (constr.GENERATOR) { + this.generatorConstraints.push(constr); + constr.visitGeneratedParams(p => this.generatedParams.set(p, constr)); + } + } + + _popConstraint(constr) { + + removeInPlace(this.constraints, constr); + + visitParams(constr, true, p => removeFromSetInMap(this.paramToConstraintsGraph, p, constr)); + visitParams(constr, false, p => removeFromSetInMap(this.paramToConstraintsIndex, p, constr)); + + if (constr.GENERATOR) { + removeInPlace(this.generatorConstraints, constr); + constr.visitGeneratedParams(p => this.generatedParams.delete(p)); + } + } +} + +export class System extends Index{ + constructor() { + super(); + this.subSystems = []; + this.constraintToSubSystem = new Map(); + this.paramToSubSystem = new Map(); + } + + _reset() { + super._reset(); + this.subSystems = []; + this.constraintToSubSystem.clear(); + this.paramToSubSystem.clear(); + } + + _collectDependenciesForSubSystemFromConstraint(subSystem, constr) { + visitParams(constr, false, p => { + let generator = this.generatedParams.get(p); + if (generator) { + let generatorSS = this.constraintToSubSystem.get(generator); + if (generatorSS) { + if (subSystem.dependencies.indexOf(generatorSS) === -1) { + subSystem.dependencies.push(generatorSS); + } + } + } + }); + } + + _rebuildDependencies() { + this.subSystems.forEach(ss => { + if (ss.dependencies.length !== 0) { + ss.dependencies = []; + } + }); + this.subSystems.forEach(subSystem => { + subSystem.constraints.forEach(constr => { + this._collectDependenciesForSubSystemFromConstraint(subSystem, constr); + }); + }); + } + + _groupBySubsystems() { + + if (this.subSystems.length !== 0) { + this.subSystems = []; + } + this.constraintToSubSystem.clear(); + + const visited = new Set(); + this.constraints.forEach(constr => { + if (visited.has(constr)) { + return; + } + const subSystem = this.createSubSystem(); + + const stack = [constr]; + while (stack.length) { + let workingConstr = stack.pop(); + if (visited.has(workingConstr)) { + continue; + } + this._assignConstraint(workingConstr, subSystem); + + visited.add(workingConstr); + visitParams(workingConstr, true, p => { + const constrs = this.paramToConstraintsGraph.get(p); + if (constrs) { + constrs.forEach(constrToAdvance => { + if (constrToAdvance !== workingConstr) { + stack.push(constrToAdvance); + } + }) + } + }); + } + }); + + } + + _rebuild() { + this._groupBySubsystems(); + this._rebuildDependencies(); + } + + _assignConstraint(constr, subSystem) { + subSystem.constraints.push(constr); + this.constraintToSubSystem.set(constr, subSystem); + } + + add(constr) { + constr.id = "C_" + (COUNTER ++) ; //fixme + let affectedSubsystems = new Set(); + let freeParams = []; + + visitParams(constr, false, p => { + + let subSystem = this.paramToSubSystem.get(p); + + if (subSystem) { + affectedSubsystems.add(subSystem); + } else { + if (!isAuxParam(p) && !this.generatedParams.has(p)) { + freeParams.push(p); + } + } + }); + affectedSubsystems.forEach(ss => { + ss.dependencies.forEach(d => affectedSubsystems.delete(d)); + }); + + let toMerge = Array.from(affectedSubsystems).sort((a, b) => a._internaOrder - b._internaOrder); + let master; + if (toMerge.length === 0 ) { + console.error("system has circular dependencies"); + master = this.createSubSystem(); + } else { + [master, ...toMerge] = toMerge; + } + + toMerge.forEach(s => { + master.mergeWith(s); + s.nativeParams.forEach(p => this.paramToSubSystem.set(p, master)); + removeInPlace(this.subSystems, s); + }); + + freeParams.forEach(p => { + master.nativeParams.add(p); + this.paramToSubSystem.set(p, master) + }); + + master.constraints.push(constr); + this.constraintToSubSystem.set(constr, master); + if (constr.GENERATOR) { + let dependant = this.createSubSystem(); + dependant.dependencies.push(master); + constr.visitGeneratedParams(p => { + this.generatedParams.set(p, constr) + this.paramToSubSystem.set(p, dependant); + }); + } + + this._pushConstraint(constr); + + } + + remove(constr) { + removeInPlace(this.constraints, constr); + this.setConstraints(this.constraints); + } + + setConstraints(constraints) { + this._reset(); + constraints.forEach(c => this.add(c)); + } + + subSystemsByParam(param, callback) { + let constraints = this.paramToConstraintsIndex.get(param); + if (constraints) { + constraints.forEach(c => callback(this.constraintToSubSystem.get(c))); + } + } + + traverse(callback, onCircular) { + const visited = new Set(); + const loop = new Set(); + + function doVisit(subSystem) { + if (loop.has(subSystem)) { + onCircular(subSystem); + return; + } + loop.add(subSystem); + subSystem.dependencies.forEach(dep => { + if (!visited.has(dep)) { + doVisit(dep); + } + }); + + callback(subSystem); + visited.add(subSystem); + loop.delete(subSystem) + } + this.subSystems.forEach(doVisit); + } + + createSubSystem() { + const subSystem = new SubSystem(); + this.subSystems.push(subSystem); + return subSystem; + } +} + +function visitParams(constraint, skipAux, callback) { + if (skipAux) { + let delegate = callback; + callback = p => { + if (!isAuxParam(p)) { + delegate(p) + } + }; + } + + if (constraint.visitParams) { + constraint.visitParams(callback); + } else { + constraint.getSolveData().forEach(([, sParams]) => sParams.forEach(callback)); + } +} + +function isAuxParam(param) { + return ParametricManager.isAux(param.obj, GOT_NOTHING); +} + +const GOT_NOTHING = { + has: () => false +}; + +let COUNTER = 0; \ No newline at end of file diff --git a/web/app/sketcher/tools/circle.js b/web/app/sketcher/tools/circle.js index 4e7b06d1..8f609795 100644 --- a/web/app/sketcher/tools/circle.js +++ b/web/app/sketcher/tools/circle.js @@ -34,7 +34,6 @@ export class EditCircleTool extends Tool { solveRequest(rough) { this.solver = this.viewer.parametricManager.prepare([this.circle.r]); this.solver.solve(rough, 1); - this.solver.sync(); } mouseup(e) { diff --git a/web/app/sketcher/tools/drag.js b/web/app/sketcher/tools/drag.js index 1bc2defb..238f1298 100644 --- a/web/app/sketcher/tools/drag.js +++ b/web/app/sketcher/tools/drag.js @@ -55,7 +55,6 @@ export class DragTool extends Tool { solveRequest(rough) { this.solver.solve(rough, 1); - this.solver.sync(); var paramsToUpdate = []; this.viewer.accept(function (obj) { @@ -72,7 +71,6 @@ export class DragTool extends Tool { this.solver.updateParameter(paramsToUpdate[i]); } this.solver.solve(rough, 1); - this.solver.sync(); } } diff --git a/web/app/sketcher/tools/ellipse.js b/web/app/sketcher/tools/ellipse.js index fde9d84a..ce77701e 100644 --- a/web/app/sketcher/tools/ellipse.js +++ b/web/app/sketcher/tools/ellipse.js @@ -113,6 +113,5 @@ export class EllipseTool extends Tool { solveRequest(rough) { this.solver = this.viewer.parametricManager.prepare([this.ellipse.r]); this.solver.solve(rough, 1); - this.solver.sync(); } } \ No newline at end of file diff --git a/web/app/sketcher/tools/fillet.js b/web/app/sketcher/tools/fillet.js index 6d8a5931..a046b171 100644 --- a/web/app/sketcher/tools/fillet.js +++ b/web/app/sketcher/tools/fillet.js @@ -67,11 +67,20 @@ export class FilletTool extends Tool { point1.parent.layer.add(arc); var pm = this.viewer.parametricManager; arc.stabilize(this.viewer); - pm._add(new Constraints.Tangent( arc, point1.parent)); - pm._add(new Constraints.Tangent( arc, point2.parent)); - pm._add(new Constraints.Coincident( arc.a, point1)); - pm._add(new Constraints.Coincident( arc.b, point2)); - + pm._add(new Constraints.Fillet( point1, point2, arc)); + + this.viewer.validators.push(() => { + function validOn(p, left) { + let op = p.parent.opposite(p); + let opV = op.toVector(); + let dir = p.toVector()._minus(opV)._normalize(); + let centerDir = arc.c.toVector()._minus(opV)._normalize(); + let z = centerDir.cross(dir).z; + + return left ? z < 0.1 : z > -0.1; + } + return validOn(point1, true) && validOn(point2, false); + }); //function otherEnd(point) { // if (point.parent.a.id === point.id) { // return point.parent.b; @@ -114,15 +123,18 @@ export class FilletTool extends Tool { } getCandidate(e) { - var picked = this.viewer.pick(e); + + let preferSketchLayer = (a, b) => (a.effectiveLayer === b.effectiveLayer)? 0 : a.effectiveLayer.name === 'sketch' ? -1 : 1; + + let picked = this.viewer.pick(e); if (picked.length > 0) { - var res = fetch.sketchObjects(picked, true, ['TCAD.TWO.EndPoint']); + let res = fetch.sketchObjects(picked, true, ['TCAD.TWO.EndPoint']); if (res == null) return null; - var point1 = res[0]; + let point1 = res.sort(preferSketchLayer)[0]; if (!FilletTool.isLine(point1.parent)) return; - var line2 = null; - for (var i = 0; i < point1.linked.length; i++) { - var point2 = point1.linked[i]; + let linked = [...point1.linked].sort(preferSketchLayer); + for (let i = 0; i < linked.length; i++) { + let point2 = linked[i]; if (FilletTool.isLine(point2.parent)) { return [point1, point2]; } diff --git a/web/app/sketcher/tools/manager.js b/web/app/sketcher/tools/manager.js index 41f7b93b..a46a3a7d 100644 --- a/web/app/sketcher/tools/manager.js +++ b/web/app/sketcher/tools/manager.js @@ -50,9 +50,7 @@ export class ToolManager { } else if (e.keyCode === 46 || e.keyCode === 8) { let selection = viewer.selected.slice(); viewer.deselectAll(); - for (let i = 0; i < selection.length; i++) { - viewer.remove(selection[i]); - } + viewer.removeAll(selection); viewer.refresh(); } }, false); diff --git a/web/app/sketcher/viewer2d.js b/web/app/sketcher/viewer2d.js index 5496aeb6..37fffd1b 100644 --- a/web/app/sketcher/viewer2d.js +++ b/web/app/sketcher/viewer2d.js @@ -79,6 +79,7 @@ function Viewer(canvas, IO) { this.historyManager = new HistoryManager(this); this.transformation = null; this.screenToModelMatrix = null; + this.validators = []; this.refresh(); } @@ -118,11 +119,11 @@ Viewer.prototype.addSegment = function(x1, y1, x2, y2, layer) { }; Viewer.prototype.remove = function(obj) { - if (obj.layer != null) { - if (obj.layer.remove(obj)) { - this.parametricManager.removeConstraintsByObj(obj); - } - } + this.removeAll([obj]); +}; + +Viewer.prototype.removeAll = function(objects) { + this.parametricManager.removeObjects(objects); }; Viewer.prototype.add = function(obj, layer) { @@ -470,7 +471,8 @@ function Layer(name, style) { this.name = name; this.style = style; this.stylesByRoles = { - 'construction': Styles.CONSTRUCTION_OF_OBJECT + 'construction': Styles.CONSTRUCTION_OF_OBJECT, + 'virtual': Styles.VIRTUAL }; this.objects = []; this.readOnly = false; // This is actually a mark for boundary layers coming from 3D diff --git a/web/sketcher.html b/web/sketcher.html index 591670eb..6a74250d 100644 --- a/web/sketcher.html +++ b/web/sketcher.html @@ -45,8 +45,9 @@
-
+
+ diff --git a/web/test/cases/solveSystems.js b/web/test/cases/solveSystems.js new file mode 100644 index 00000000..43377ce2 --- /dev/null +++ b/web/test/cases/solveSystems.js @@ -0,0 +1,216 @@ +import {assertEquals, assertFalse, assertTrue} from '../utils/asserts'; +import {NOOP} from '../../../modules/gems/func'; + +export const TEST_MODE = 'sketcherUI'; + +export function testEqualConstraints(env, ui) { + + ui.addRectangle(10, 10, 100, 100); + + assertEquals(4, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(4, ui.viewer.parametricManager.system.constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + env.done(); +} + +export function testBuildGraphBasics(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + ui.select([seg1, seg2]); + ui.runAction('parallelConstraint'); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(1, ui.viewer.parametricManager.system.constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + + const seg3 = ui.addSegment(500, 10, 500, 100); + const seg4 = ui.addSegment(700, 10, 700, 100); + ui.select([seg3, seg4]); + ui.runAction('parallelConstraint'); + + assertEquals(2, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(2, ui.viewer.parametricManager.system.constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[1].constraints.length); + + env.done(); +} + +export function testThreeConnectedConstraints(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + ui.select([seg1, seg2]); + ui.runAction('parallelConstraint'); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(1, ui.viewer.parametricManager.system.constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + + const seg3 = ui.addSegment(500, 10, 500, 100); + const seg4 = ui.addSegment(700, 10, 700, 100); + ui.select([seg3, seg4]); + ui.runAction('parallelConstraint'); + + assertEquals(2, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(2, ui.viewer.parametricManager.system.constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + assertEquals(1, ui.viewer.parametricManager.system.subSystems[1].constraints.length); + + ui.select([seg2, seg3]); + ui.runAction('parallelConstraint'); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(3, ui.viewer.parametricManager.system.constraints.length); + assertEquals(3, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + env.done(); +} + +export function testIgnoreBoundaries(env, ui) { + + const boundary = ui.addSegment(500, 10, 500, 100); + boundary.aux = true; + + const seg1 = ui.addSegment(100, 10, 100, 100); + ui.select([seg1, boundary]); + ui.runAction('parallelConstraint'); + + const seg2 = ui.addSegment(700, 10, 700, 100); + ui.select([seg1, boundary]); + + ui.select([seg1, boundary]); + ui.runAction('parallelConstraint'); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(2, ui.viewer.parametricManager.system.constraints.length); + assertEquals(2, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + env.done(); +} + +export function testMirroring(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + + ui.select([seg2, seg1]); + ui.runAction('mirrorConstraint'); + const seg3 = ui.viewer.parametricManager.system.constraints[0].reflectedObjects[0]; + const seg4 = ui.addSegment(600, 10, 600, 100); + ui.select([seg4, seg3]); + ui.runAction('mirrorConstraint'); + + let system = ui.viewer.parametricManager.system; + let subSystems = ui.viewer.parametricManager.system.subSystems; + assertEquals(3, subSystems.length); + const subSystem0 = system.constraintToSubSystem.get(system.constraints[0]); + const subSystem1 = system.constraintToSubSystem.get(system.constraints[1]); + assertTrue( subSystem1.dependencies[0] === subSystem0, "Second subsystem depends on first one"); + + env.done(); +} + +export function testCircularDependencies(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + + ui.select([seg2, seg1]); + ui.runAction('mirrorConstraint'); + const seg3 = ui.viewer.parametricManager.system.constraints[0].reflectedObjects[0]; + const seg4 = ui.addSegment(600, 10, 600, 100); + ui.select([seg4, seg3]); + ui.runAction('mirrorConstraint'); + + + let seg5 = ui.viewer.parametricManager.system.constraints[1].reflectedObjects[0]; + ui.select([seg5, seg1]); + ui.runAction('parallelConstraint'); + + let isCircular = false; + ui.viewer.parametricManager.system.traverse(NOOP, () => isCircular = true); + + assertEquals(2, ui.viewer.parametricManager.system.subSystems.length); + assertTrue(isCircular, "System should contain circular depending subsystems"); + + env.done(); +} + + +export function testSimpleRemove(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + ui.select([seg1, seg2]); + ui.runAction('parallelConstraint'); + + const seg3 = ui.addSegment(500, 10, 500, 100); + const seg4 = ui.addSegment(700, 10, 700, 100); + ui.select([seg3, seg4]); + ui.runAction('parallelConstraint'); + + + ui.select([seg2, seg3]); + ui.runAction('parallelConstraint'); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(3, ui.viewer.parametricManager.system.constraints.length); + assertEquals(3, ui.viewer.parametricManager.system.subSystems[0].constraints.length); + + ui.select([]); + ui.viewer.remove(seg1); + + assertEquals(1, ui.viewer.parametricManager.system.subSystems.length); + assertEquals(2, ui.viewer.parametricManager.system.constraints.length); + + env.done(); +} + +export function testMirroringRemove(env, ui) { + + const seg1 = ui.addSegment(10, 10, 10, 100); + const seg2 = ui.addSegment(200, 10, 200, 100); + + ui.select([seg2, seg1]); + ui.runAction('mirrorConstraint'); + const seg3 = ui.viewer.parametricManager.system.constraints[0].reflectedObjects[0]; + const seg4 = ui.addSegment(600, 10, 600, 100); + ui.select([seg4, seg3]); + ui.runAction('mirrorConstraint'); + + assertEquals(3, ui.viewer.parametricManager.system.subSystems.length); + ui.viewer.remove(seg1); + assertEquals(0, ui.viewer.parametricManager.system.subSystems.length); + + env.done(); +} + +export function testDoubleAngle(env, ui) { + + const seg1 = ui.addSegment(100, 100, 100, 200); + const seg2 = ui.addSegment(100, 200, 200, 200); + const seg3 = ui.addSegment(10, 10, 300, 10); + + ui.select([seg3, seg2, seg1]); + + ui.runAction('mirrorConstraint'); + + const [seg4, seg5] = ui.viewer.parametricManager.system.constraints[1].reflectedObjects; + const seg6 = ui.addSegment(300, -200, 300, 300); + + ui.select([seg6, seg5, seg4, seg2, seg1]); + + ui.runAction('mirrorConstraint'); + + let isCircular = false; + ui.viewer.parametricManager.system.traverse(NOOP, () => isCircular = true); + + assertFalse(isCircular, "shouldn't be circular"); + + env.done(); +} diff --git a/web/test/modes.js b/web/test/modes.js index 42f9f636..627cf4aa 100644 --- a/web/test/modes.js +++ b/web/test/modes.js @@ -1,5 +1,6 @@ import * as test from './test'; import modellerUISubject from './utils/subjects/modeller/modellerUISubject'; +import {createSketcherSubject} from './utils/subjects/modeller/sketcherUISubject'; export const modellerUI = func => env => { test.emptyModeller(env.test(win => { @@ -8,3 +9,9 @@ export const modellerUI = func => env => { })); }; +export const sketcherUI = func => env => { + test.emptySketch(env.test((win, app) => { + let subject = createSketcherSubject(app); + func(env, subject); + })); +}; diff --git a/web/test/runner.html b/web/test/runner.html index 392200f1..3a51c2c4 100644 --- a/web/test/runner.html +++ b/web/test/runner.html @@ -3,6 +3,7 @@ +
diff --git a/web/test/runner.less b/web/test/runner.less index 3d10f65d..212bee8a 100644 --- a/web/test/runner.less +++ b/web/test/runner.less @@ -14,6 +14,7 @@ body { width: 70%; height: 70%; background-color: #233930; + //visibility: hidden; } #test-list { diff --git a/web/test/suites.js b/web/test/suites.js index 4b7b018f..8b9a75fd 100644 --- a/web/test/suites.js +++ b/web/test/suites.js @@ -24,6 +24,7 @@ export default { SketcherSolver: [ TestCase('constraints'), TestCase('parametric'), + TestCase('solveSystems'), ], diff --git a/web/test/test.js b/web/test/test.js index 506c3c18..46763f2d 100644 --- a/web/test/test.js +++ b/web/test/test.js @@ -1,7 +1,8 @@ -export function FailError(msg) { - this.msg = msg; - this.stack = (new Error()).stack; +export function createFailError(msg) { + let error = new Error(msg); + error.assertionFail = true; + return error; } export class TestEnv { @@ -27,7 +28,7 @@ export class TestEnv { this.failed = true; this.error = msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg); this.done(); - throw new FailError(this.error); + throw createFailError(this.error); } terminateOnError(error) { @@ -46,8 +47,7 @@ export class TestEnv { if (!env.finished) { env.terminateOnError(e); } - console.error(e.stack); - throw e; + console.error(e); } } } diff --git a/web/test/utils/asserts.js b/web/test/utils/asserts.js index e926ac55..19b2bce7 100644 --- a/web/test/utils/asserts.js +++ b/web/test/utils/asserts.js @@ -1,17 +1,23 @@ -import {FailError} from '../test'; +import {createFailError} from '../test'; import sketchObjectGlobalId from '../../app/cad/sketch/sketchObjectGlobalId'; export function fail(msg, optionalMsg) { - throw new FailError(msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg)); + throw createFailError(msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg)); } export function assertTrue(stmt, msg) { + if (typeof stmt === 'string') { + throw 'wrong assertion usage, mixed up arguments'; + } if (!stmt) { fail('assertTrue fails.', msg); } } export function assertEmpty(array, msg) { + if (typeof stmt === 'string') { + throw 'wrong assertion usage, mixed up arguments'; + } if (array.length !== 0) { fail('assertEmpty fails. Array length = ' + array.length, msg); } diff --git a/web/test/utils/sketcher-utils.js b/web/test/utils/sketcher-utils.js index 6ef4c442..9bdde294 100644 --- a/web/test/utils/sketcher-utils.js +++ b/web/test/utils/sketcher-utils.js @@ -10,11 +10,7 @@ export function toModelP(app, point) { } export function getConstraints(app) { - const subSystems = app.viewer.parametricManager.subSystems; - if (subSystems.length == 0) { - return []; - } - return subSystems[0].constraints; + return app.viewer.parametricManager.system.constraints; } export function click(app, point, attrs) { @@ -170,10 +166,11 @@ export class TestSegment { } } -function modelToScreen(viewer, x, y) { - - let modelToScreenMx = viewer.screenToModelMatrix.invert(); - [x, y] = modelToScreenMx.apply3([x, y, 0]); +export function modelToScreen(viewer, x, y) { + if (viewer.screenToModelMatrix) { + let modelToScreenMx = viewer.screenToModelMatrix.invert(); + [x, y] = modelToScreenMx.apply3([x, y, 0]); + } x /= viewer.retinaPxielRatio; y = (viewer.canvas.height - y) / viewer.retinaPxielRatio; return [x, y]; diff --git a/web/test/utils/subjects/modeller/sketcherUISubject.js b/web/test/utils/subjects/modeller/sketcherUISubject.js index a3e67cf0..2bdd79b8 100644 --- a/web/test/utils/subjects/modeller/sketcherUISubject.js +++ b/web/test/utils/subjects/modeller/sketcherUISubject.js @@ -4,7 +4,6 @@ import genSerpinski, {genSerpinskiImpl} from '../../../../app/utils/genSerpinski import {distance, distanceAB} from '../../../../app/math/math'; export function createSubjectFromInPlaceSketcher(ctx) { - let actions = {}; for (const actionId of Object.keys(ctx.streams.action.state)) { if (actionId.startsWith('sketch')) { @@ -15,19 +14,27 @@ export function createSubjectFromInPlaceSketcher(ctx) { actions.addBezierCurve = actions.addCubicBezierSpline; } } - + const oldStyleSketcherApp = { viewer: ctx.services.sketcher.inPlaceEditor.viewer, actions }; + + return createSketcherSubject(oldStyleSketcherApp); +} - const addSegment = sketcher_utils.addSegmentInModel.bind(this, oldStyleSketcherApp); - const addArc = sketcher_utils.addArc.bind(this, oldStyleSketcherApp); - const addCircle = sketcher_utils.addCircle.bind(this, oldStyleSketcherApp); - const addEllipse = sketcher_utils.addEllipse.bind(this, oldStyleSketcherApp); - const addEllipticalArc = sketcher_utils.addEllipticalArc.bind(this, oldStyleSketcherApp); - const addBezier = sketcher_utils.addBezier.bind(this, oldStyleSketcherApp); - const move = sketcher_utils.moveInModel.bind(this, oldStyleSketcherApp); +export function createSketcherSubject(sketcherApp) { + + const viewer = sketcherApp.viewer; + viewer.parametricManager.messageSink = msg => console.log(msg); + + const addSegment = sketcher_utils.addSegmentInModel.bind(this, sketcherApp); + const addArc = sketcher_utils.addArc.bind(this, sketcherApp); + const addCircle = sketcher_utils.addCircle.bind(this, sketcherApp); + const addEllipse = sketcher_utils.addEllipse.bind(this, sketcherApp); + const addEllipticalArc = sketcher_utils.addEllipticalArc.bind(this, sketcherApp); + const addBezier = sketcher_utils.addBezier.bind(this, sketcherApp); + const move = sketcher_utils.moveInModel.bind(this, sketcherApp); function addRectangle(x0, y0, x1, y1) { return [ addSegment(x0, y0, x1, y0), @@ -38,7 +45,7 @@ export function createSubjectFromInPlaceSketcher(ctx) { } function addSerpinski(ax, ay, bx, by, depth) { - genSerpinskiImpl(ctx.services.sketcher.inPlaceEditor.viewer, {x: ax, y: ay}, {x: bx, y: by}, depth); + genSerpinskiImpl(viewer, {x: ax, y: ay}, {x: bx, y: by}, depth); let jointWidth = distance(ax, ay, bx, by) / (depth + 1) / 2; let dx = bx - ax; let dy = by - ay; @@ -47,7 +54,7 @@ export function createSubjectFromInPlaceSketcher(ctx) { dy /= D; let ddx = -dy * jointWidth; let ddy = dx * jointWidth; - genSerpinskiImpl(ctx.services.sketcher.inPlaceEditor.viewer, {x: bx-ddx, y: by-ddy}, {x: ax-ddx, y: ay-ddy}, depth); + genSerpinskiImpl(viewer, {x: bx-ddx, y: by-ddy}, {x: ax-ddx, y: ay-ddy}, depth); addSegment(ax, ay, ax-ddx, ay-ddy); addSegment(bx, by, bx-ddx, by-ddy); } @@ -62,7 +69,7 @@ export function createSubjectFromInPlaceSketcher(ctx) { } function changeLayer(layerName) { - ctx.services.sketcher.inPlaceEditor.viewer.setActiveLayerName(layerName); + viewer.setActiveLayerName(layerName); } function changeToConstructionLayer() { @@ -73,9 +80,24 @@ export function createSubjectFromInPlaceSketcher(ctx) { changeLayer('sketch'); } + function click(modelX, modelY, attrs) { + let [x, y] = sketcher_utils.modelToScreen(viewer, modelX, modelY); + sketcher_utils.clickXY(sketcherApp, x, y, attrs); + } + + function select(objects, inclusive) { + sketcherApp.viewer.select(objects, !inclusive); + } + + function runAction(id) { + sketcherApp.actions[id].action(); + } + return { addSegment, addRectangle, addArc, addCircle, addEllipse, addEllipticalArc, addSerpinski, addBezier, addPolygon, - move, changeLayer, changeToConstructionLayer, changeToDefaultLayer + move, changeLayer, changeToConstructionLayer, changeToDefaultLayer, + click, select, runAction, + viewer } } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 1ad3e5f4..98afaded 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -31,6 +31,20 @@ module.exports = { extensions: ['.js', '.jsx'], modules: [MODULES, "node_modules"] }, + devServer: { + hot: false, + inline: false, + before: function(app) { + app.get('*.wasm', function(req, res) { + res.sendFile(req.url, { + root: path.join(__dirname, 'web'), + headers: { + 'Content-Type': 'application/wasm' + } + }); + }); + }, + }, module: { rules: [{ test: /\.(js|jsx)$/, @@ -45,39 +59,25 @@ module.exports = { 'less-loader', ] }, - { - test: /\.(less|css)$/, - include: [MODULES, WEB_APP], - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - getLocalIdent: (context, localIdentName, localName) => generateCSSScopedName(localName, context.resourcePath), - modules: true, - url: false - } - }, - 'less-loader' - ] - }, - { - test: /\.html$/, - use: 'handlebars-loader?helperDirs[]=' + __dirname + '/web/app/ui/helpers' - }] - }, - devServer: { - hot: false, - inline: false, - before: function(app) { - app.get('*.wasm', function(req, res) { - res.sendFile(req.url, { - root: path.join(__dirname, 'web'), - headers: { - 'Content-Type': 'application/wasm' - } - }); - }); - }, + { + test: /\.(less|css)$/, + include: [MODULES, WEB_APP], + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + getLocalIdent: (context, localIdentName, localName) => generateCSSScopedName(localName, context.resourcePath), + modules: true, + url: false + } + }, + 'less-loader' + ] + }, + { + test: /\.html$/, + use: 'handlebars-loader?helperDirs[]=' + __dirname + '/web/app/ui/helpers' + }] } };