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