assembly infrastructure

This commit is contained in:
Val Erastov (xibyte) 2020-06-22 02:15:18 -07:00
parent 4ddf018fc6
commit 931a05ac36
33 changed files with 885 additions and 545 deletions

View file

@ -0,0 +1,23 @@
import * as React from "react";
import {BsCheckCircle, BsQuestionCircle} from "react-icons/bs";
import {FaRegTimesCircle} from "react-icons/fa";
export function Status({success}: {
success?: boolean
}) {
if (success === true) {
return <span><BsCheckCircle style={{
color: 'green'
}}/> success</span>
} else if (success === false) {
return <span><FaRegTimesCircle style={{
color: 'red'
}}/> fail</span>
} else {
return <span><BsQuestionCircle style={{
color: 'orange'
}}/> unknown</span>
}
}

View file

@ -29,3 +29,11 @@ pre {
path {
stroke: currentColor;
}
.error-text {
color: @on-color-highlight-variant-red;
}
.warning-text {
color: @on-color-highlight-variant-yellow;
}

View file

@ -1,10 +1,8 @@
import {MObject} from "../model/mobject";
import {Param} from "../../sketcher/shapes/param";
import Vector from "math/vector";
import {Matrix3} from "math/l3space";
import {ISolveStage, SolvableObject} from "../../sketcher/constr/solvableObject";
import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints";
import {Constraints3D} from "./constraints3d";
import {AssemblyCSysNode} from "./nodes/assemblyCSysNode";
export abstract class AssemblyNode implements SolvableObject {
@ -41,105 +39,3 @@ export abstract class AssemblyNode implements SolvableObject {
}
export class AssemblyUnitVectorNode extends AssemblyNode {
x = new Param(0, 'X');
y = new Param(0, 'Y');
z = new Param(0, 'Z');
getVector: () => Vector;
constructor(model: MObject, getVector: () => Vector) {
super(model);
this.getVector = getVector;
}
visitParams(cb) {
cb(this.x);
cb(this.y);
cb(this.z);
}
reset() {
const {x, y, z} = this.getVector();
this.x.set(x);
this.y.set(y);
this.z.set(z);
}
createConsistencyConstraints() {
return [
new AlgNumConstraint(Constraints3D.UnitVectorConsistency, [this])
];
}
createRigidBodyLink(body: AssemblyCSysNode) {
return [
new AlgNumConstraint(Constraints3D.RigidBodyLink3x3, [body, this])
];
}
}
export class AssemblyCSysNode extends AssemblyNode {
ox = new Param(0, 'X');
oy = new Param(0, 'Y');
oz = new Param(0, 'Z');
ix = new Param(1, 'X');
iy = new Param(0, 'Y');
iz = new Param(0, 'Z');
jx = new Param(0, 'X');
jy = new Param(1, 'Y');
jz = new Param(0, 'Z');
kx = new Param(0, 'X');
ky = new Param(0, 'Y');
kz = new Param(1, 'Z');
getTransformation: () => Matrix3;
constructor(model: MObject, getTransformation: () => Matrix3) {
super(model);
this.getTransformation = getTransformation;
}
visitParams(cb) {
cb(this.ox);
cb(this.oy);
cb(this.oz);
cb(this.ix);
cb(this.iy);
cb(this.iz);
cb(this.jx);
cb(this.jy);
cb(this.jz);
cb(this.kx);
cb(this.ky);
cb(this.kz);
}
reset() {
const mx = this.getTransformation();
this.ox.set(mx.tx);
this.oy.set(mx.ty);
this.oz.set(mx.tz);
this.ix.set(mx.mxx);
this.iy.set(mx.myx);
this.iz.set(mx.mzx);
this.jx.set(mx.mxy);
this.jy.set(mx.myy);
this.jz.set(mx.mzy);
this.kx.set(mx.mxz);
this.ky.set(mx.myz);
this.kz.set(mx.mzz);
}
createConsistencyConstraints() {
return [
new AlgNumConstraint(Constraints3D.CSysConsistency, [this])
];
}
}

View file

@ -0,0 +1,10 @@
import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints";
export interface AssemblyConstraintDefinition {
typeId: string;
objects: string[];
constants: ConstantsDefinitions
}

View file

@ -0,0 +1,118 @@
import {ApplicationContext} from "context";
import {ModellerContextualActions} from "./ui/ModellerContextualActions";
import {state, StateStream} from "lstream";
import {AssemblyConstraintDefinition} from "./assemblyConstraintDefinition";
import {solveAssembly as solveAssemblyImpl} from "./assemblySolver";
import {Constraints3D, createAssemblyConstraint} from "./constraints3d";
import {SolveStatus} from "../../sketcher/constr/AlgNumSystem";
import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints";
import {AssemblyView} from "./ui/AssemblyView";
import {IoMdConstruct} from "react-icons/io";
export function activate(ctx: ApplicationContext) {
const constraints$ = state<AssemblyConstraintDefinition[][]>([]);
const status$ = state<SolveStatus>(null);
function getConstraints(): AssemblyConstraintDefinition[][] {
return constraints$.value;
}
function loadConstraints(inData: AssemblyConstraintDefinition[][]): void {
constraints$.next(inData);
}
function addConstraint(typeId: string, objects: string[], constants?: ConstantsDefinitions): void {
constraints$.mutate(stages => {
if (stages.length === 0) {
stages.push([])
}
stages[stages.length - 1].push({
typeId, objects, constants
});
})
}
function removeConstraint(constr: AssemblyConstraintDefinition) {
constraints$.mutate(stages => {
for (let constrs of stages) {
const index = constrs.indexOf(constr);
if (index !== -1) {
constrs.splice(index, 1);
}
}
})
}
function solveAssembly(): void {
if (ctx.craftService.isEditingHistory()) {
console.log('skipping assembly resolve request in the history mode');
return;
}
const stages = constraints$.value.map(stage => stage.map(constr => {
const schema = Constraints3D[constr.typeId];
if (!schema) {
console.error('reference to nonexistent constraint ' + constr.typeId);
return null;
}
const objects = [];
for (const id of constr.objects) {
const modelObject = ctx.cadRegistry.find(id);
if (!modelObject) {
console.warn('skipping constraint referring to nonexistent object ' + id);
return null;
}
objects.push(modelObject);
}
return createAssemblyConstraint(schema, objects)
} ).filter(x => x) );
const solveStatus = solveAssemblyImpl(stages);
status$.next(solveStatus);
}
constraints$.attach(solveAssembly);
ctx.domService.contributeComponent(ModellerContextualActions);
ctx.services.ui.registerFloatView('assembly', AssemblyView, 'Assembly', IoMdConstruct);
ctx.craftService.modifications$.attach((modifications) => {
//if we reach the end reevaluate locations
if (modifications.pointer === modifications.history.length - 1) {
solveAssembly();
}
});
ctx.assemblyService = {
constraints$, getConstraints, loadConstraints, solveAssembly, addConstraint, removeConstraint, status$
}
}
export interface AssemblyService {
constraints$: StateStream<AssemblyConstraintDefinition[][]>;
status$: StateStream<SolveStatus>;
addConstraint(typeId: string, objects: string[], constants?: ConstantsDefinitions): void;
removeConstraint(constr: AssemblyConstraintDefinition): void;
solveAssembly(): void;
loadConstraints(constraints: AssemblyConstraintDefinition[][]);
getConstraints(): AssemblyConstraintDefinition[][];
}
declare module 'context' {
interface ApplicationContext {
assemblyService: AssemblyService;
}
}

View file

@ -1,17 +1,18 @@
import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints";
import {AlgNumSubSystem} from "../../sketcher/constr/AlgNumSystem";
import {AlgNumSubSystem, SolveStatus} from "../../sketcher/constr/AlgNumSystem";
import Vector from "math/vector";
import CSys from "math/csys";
import {AssemblyCSysNode, AssemblyNode} from "./assembly";
import {AssemblyNode} from "./assembly";
import {ISolveStage} from "../../sketcher/constr/solvableObject";
import {MObject} from "../model/mobject";
import {MShell} from "../model/mshell";
import {Constraints3D} from "./constraints3d";
import {AssemblyCSysNode} from "./nodes/assemblyCSysNode";
export function solveAssembly(constraints: AlgNumConstraint[]) {
export function solveAssembly(stages: AlgNumConstraint[][]): SolveStatus {
// temporary solve everything in one stage
const constraints = [].concat(...stages);
const objects = new Set<AssemblyNode>();
constraints.forEach(c => c.objects.forEach(o => objects.add(o)));
const stage: ISolveStage = {
@ -33,11 +34,8 @@ export function solveAssembly(constraints: AlgNumConstraint[]) {
o.reset();
});
// const algNumConstraint = new AlgNumConstraint(Constraints3D.FaceParallel, objects);
const system = new AlgNumSubSystem(() => 0.001, val => val, stage);
// __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get())))
// __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get())))
system.startTransaction();
constraints.forEach(c => system.addConstraint(c));
@ -60,17 +58,18 @@ export function solveAssembly(constraints: AlgNumConstraint[]) {
}
});
system.prepare();
system.solveFine();
system.finishTransaction();
system.solveFine();
// __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get())))
// __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get())))
roots.forEach(root => {
applyResults(root, root.assemblyNodes.location);
});
if (system.solveStatus.success) {
roots.forEach(root => {
applyResults(root, root.assemblyNodes.location);
});
} else {
console.log("Assembly system haven't been solved, locations won't be updated");
}
return system.solveStatus;
}

View file

@ -3,6 +3,11 @@ import {NoIcon} from "../../sketcher/icons/NoIcon";
import {AlgNumConstraint, ConstantsDefinitions, ConstraintSchema} from "../../sketcher/constr/ANConstraints";
import {MObject} from "../model/mobject";
import {SolvableObject} from "../../sketcher/constr/solvableObject";
import {AssemblyNode} from "./assembly";
import {EndPoint} from "../../sketcher/shapes/point";
import {Circle} from "../../sketcher/shapes/circle";
import {Arc} from "../../sketcher/shapes/arc";
export const Constraints3D = {
@ -47,13 +52,70 @@ export const Constraints3D = {
},
FaceToFace: {
id: 'FaceToFace',
name: 'Face To Face',
icon: NoIcon,
selectionMatcher: {
selector: 'matchAll',
types: ['face'],
minQuantity: 2
},
defineAssemblyScope: ([face1, face2]) => {
return [
face1.assemblyNodes.plane,
face2.assemblyNodes.plane,
];
},
defineParamsScope: ([plane1, plane2], cb) => {
plane1.visitParams(cb);
plane2.visitParams(cb);
},
collectPolynomials: (polynomials, params) => {
const [
nx1, ny1, nz1, w1, nx2, ny2, nz2, w2
] = params;
polynomials.push(
new Polynomial(1)
.monomial()
.term(nx1, POW_1_FN)
.term(nx2, POW_1_FN)
.monomial()
.term(ny1, POW_1_FN)
.term(ny2, POW_1_FN)
.monomial()
.term(nz1, POW_1_FN)
.term(nz2, POW_1_FN)
);
polynomials.push(
new Polynomial()
.monomial()
.term(w1, POW_1_FN)
.monomial()
.term(w2, POW_1_FN)
);
}
},
UnitVectorConsistency: {
id: 'UnitVectorConsistency',
name: 'UnitVectorConsistency',
icon: NoIcon,
defineParamsScope: ([vec], cb) => {
vec.visitParams(cb);
//don't change to generic way it can a plane
cb(vec.x);
cb(vec.y);
cb(vec.z);
},
collectPolynomials: (polynomials, params) => {
@ -162,6 +224,80 @@ export const Constraints3D = {
},
},
RigidBodyPlaneLink: {
id: 'RigidBodyPlaneLink',
name: 'RigidBodyPlaneLink',
icon: NoIcon,
defineParamsScope: ([csys, plane], cb) => {
csys.visitParams(cb);
plane.visitParams(cb);
},
collectPolynomials: (polynomials, params, _, objects) => {
const [csys, plane] = objects;
const n = plane.getNormal();
const wStar = plane.getDepth();
const {x: xStar, y: yStar, z: zStar} = n.multiply(wStar);
const [ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz, x, y, z, w] = params;
// out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx;
// out.y = this.myx * x + this.myy * y + this.myz * z + this.ty;
// out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz;
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(x, POW_1_FN)
.term(w, POW_1_FN)
.monomial(xStar)
.term(ix, POW_1_FN)
.monomial(yStar)
.term(jx, POW_1_FN)
.monomial(zStar)
.term(kx, POW_1_FN)
.monomial()
.term(ox, POW_1_FN)
);
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(y, POW_1_FN)
.term(w, POW_1_FN)
.monomial(xStar)
.term(iy, POW_1_FN)
.monomial(yStar)
.term(jy, POW_1_FN)
.monomial(zStar)
.term(ky, POW_1_FN)
.monomial()
.term(oy, POW_1_FN)
);
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(z, POW_1_FN)
.term(w, POW_1_FN)
.monomial(xStar)
.term(iz, POW_1_FN)
.monomial(yStar)
.term(jz, POW_1_FN)
.monomial(zStar)
.term(kz, POW_1_FN)
.monomial()
.term(oz, POW_1_FN)
);
}
},
RigidBodyLink3x3: {
id: 'RigidBodyLink3x3',
name: 'RigidBodyLink3x3',
@ -231,18 +367,103 @@ export const Constraints3D = {
}
},
RigidBodyLink4x4: {
id: 'RigidBodyLink4x4',
name: 'RigidBodyLink4x4',
icon: NoIcon,
defineParamsScope: ([csys, vec], cb) => {
cb(csys.ox);
cb(csys.oy);
cb(csys.oz);
cb(csys.ix);
cb(csys.iy);
cb(csys.iz);
cb(csys.jx);
cb(csys.jy);
cb(csys.jz);
cb(csys.kx);
cb(csys.ky);
cb(csys.kz);
vec.visitParams(cb);
},
collectPolynomials: (polynomials, params, _, objects) => {
const [csys, vec] = objects;
const {x: xStar, y: yStar, z: zStar} = vec.getVector();
const [ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz, x, y, z] = params;
// out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx;
// out.y = this.myx * x + this.myy * y + this.myz * z + this.ty;
// out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz;
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(x, POW_1_FN)
.monomial(xStar)
.term(ix, POW_1_FN)
.monomial(yStar)
.term(jx, POW_1_FN)
.monomial(zStar)
.term(kx, POW_1_FN)
.monomial()
.term(ox, POW_1_FN)
);
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(y, POW_1_FN)
.monomial(xStar)
.term(iy, POW_1_FN)
.monomial(yStar)
.term(jy, POW_1_FN)
.monomial(zStar)
.term(ky, POW_1_FN)
.monomial()
.term(oy, POW_1_FN)
);
polynomials.push(
new Polynomial(0)
.monomial(-1)
.term(z, POW_1_FN)
.monomial(xStar)
.term(iz, POW_1_FN)
.monomial(yStar)
.term(jz, POW_1_FN)
.monomial(zStar)
.term(kz, POW_1_FN)
.monomial()
.term(oz, POW_1_FN)
);
}
},
};
export interface AssemblyConstraintSchema extends ConstraintSchema {
defineAssemblyScope: (objects: MObject[]) => SolvableObject[],
selectionMatcher?: {
selector: string,
types: any[],
minQuantity: number
};
defineAssemblyScope: (objects: MObject[]) => AssemblyNode[],
}
export function createAssemblyConstraint(schema: AssemblyConstraintSchema,
objects: MObject[],
constants?: ConstantsDefinitions,
internal?: boolean = false) {
internal: boolean = false) {
return new AlgNumConstraint(schema, schema.defineAssemblyScope(objects), constants, internal);
}

View file

@ -0,0 +1,70 @@
import {Param} from "../../../sketcher/shapes/param";
import {Matrix3} from "math/l3space";
import {MObject} from "../../model/mobject";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Constraints3D} from "../constraints3d";
import {AssemblyNode} from "../assembly";
export class AssemblyCSysNode extends AssemblyNode {
ox = new Param(0, 'X');
oy = new Param(0, 'Y');
oz = new Param(0, 'Z');
ix = new Param(1, 'X');
iy = new Param(0, 'Y');
iz = new Param(0, 'Z');
jx = new Param(0, 'X');
jy = new Param(1, 'Y');
jz = new Param(0, 'Z');
kx = new Param(0, 'X');
ky = new Param(0, 'Y');
kz = new Param(1, 'Z');
getTransformation: () => Matrix3;
constructor(model: MObject, getTransformation: () => Matrix3) {
super(model);
this.getTransformation = getTransformation;
}
visitParams(cb) {
cb(this.ox);
cb(this.oy);
cb(this.oz);
cb(this.ix);
cb(this.iy);
cb(this.iz);
cb(this.jx);
cb(this.jy);
cb(this.jz);
cb(this.kx);
cb(this.ky);
cb(this.kz);
}
reset() {
const mx = this.getTransformation();
this.ox.set(mx.tx);
this.oy.set(mx.ty);
this.oz.set(mx.tz);
this.ix.set(mx.mxx);
this.iy.set(mx.myx);
this.iz.set(mx.mzx);
this.jx.set(mx.mxy);
this.jy.set(mx.myy);
this.jz.set(mx.mzy);
this.kx.set(mx.mxz);
this.ky.set(mx.myz);
this.kz.set(mx.mzz);
}
createConsistencyConstraints() {
return [
new AlgNumConstraint(Constraints3D.CSysConsistency, [this])
];
}
}

View file

@ -0,0 +1,53 @@
import {Param} from "../../../sketcher/shapes/param";
import Vector from "math/vector";
import {MObject} from "../../model/mobject";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Constraints3D} from "../constraints3d";
import {AssemblyNode} from "../assembly";
import {AssemblyCSysNode} from "./assemblyCSysNode";
export class AssemblyPlaneNode extends AssemblyNode {
x = new Param(0, 'X');
y = new Param(0, 'Y');
z = new Param(0, 'Z');
w = new Param(0, 'W');
getNormal: () => Vector;
getDepth: () => number;
constructor(model: MObject, getNormal: () => Vector, getDepth: () => number) {
super(model);
this.getNormal = getNormal;
this.getDepth = getDepth;
}
visitParams(cb) {
cb(this.x);
cb(this.y);
cb(this.z);
cb(this.w);
}
reset() {
const {x, y, z} = this.getNormal();
const w = this.getDepth();
this.x.set(x);
this.y.set(y);
this.z.set(z);
this.w.set(w);
}
createConsistencyConstraints() {
return [
new AlgNumConstraint(Constraints3D.UnitVectorConsistency, [this])
];
}
createRigidBodyLink(body: AssemblyCSysNode) {
return [
new AlgNumConstraint(Constraints3D.RigidBodyPlaneLink, [body, this])
];
}
}

View file

@ -0,0 +1,33 @@
import {AssemblyNode} from "../assembly";
import {Param} from "../../../sketcher/shapes/param";
import {MObject} from "../../model/mobject";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Constraints3D} from "../constraints3d";
import {AssemblyCSysNode} from "./assemblyCSysNode";
export class AssemblyScalarNode extends AssemblyNode {
param: Param;
getValue: () => number;
constructor(model: MObject, debugSymbol: string, getValue: () => number) {
super(model);
this.param = new Param(0, debugSymbol);
this.getValue = getValue;
}
reset() {
this.param.set(this.getValue());
}
visitParams(cb) {
cb(this.param);
}
createRigidBodyLink(body: AssemblyCSysNode) {
return [
// new AlgNumConstraint(Constraints3D.RigidTest, [body, this.model.assemblyNodes.normal, this])
];
}
}

View file

@ -0,0 +1,46 @@
import {Param} from "../../../sketcher/shapes/param";
import Vector from "math/vector";
import {MObject} from "../../model/mobject";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Constraints3D} from "../constraints3d";
import {AssemblyNode} from "../assembly";
import {AssemblyCSysNode} from "./assemblyCSysNode";
export class AssemblyUnitVectorNode extends AssemblyNode {
x = new Param(0, 'X');
y = new Param(0, 'Y');
z = new Param(0, 'Z');
getVector: () => Vector;
constructor(model: MObject, getVector: () => Vector) {
super(model);
this.getVector = getVector;
}
visitParams(cb) {
cb(this.x);
cb(this.y);
cb(this.z);
}
reset() {
const {x, y, z} = this.getVector();
this.x.set(x);
this.y.set(y);
this.z.set(z);
}
createConsistencyConstraints() {
return [
new AlgNumConstraint(Constraints3D.UnitVectorConsistency, [this])
];
}
createRigidBodyLink(body: AssemblyCSysNode) {
return [
// new AlgNumConstraint(Constraints3D.RigidBodyLink3x3, [body, this])
];
}
}

View file

@ -0,0 +1,40 @@
import {Param} from "../../../sketcher/shapes/param";
import Vector from "math/vector";
import {MObject} from "../../model/mobject";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Constraints3D} from "../constraints3d";
import {AssemblyNode} from "../assembly";
import {AssemblyCSysNode} from "./assemblyCSysNode";
export class AssemblyVectorNode extends AssemblyNode {
x = new Param(0, 'X');
y = new Param(0, 'Y');
z = new Param(0, 'Z');
getVector: () => Vector;
constructor(model: MObject, getVector: () => Vector) {
super(model);
this.getVector = getVector;
}
visitParams(cb) {
cb(this.x);
cb(this.y);
cb(this.z);
}
reset() {
const {x, y, z} = this.getVector();
this.x.set(x);
this.y.set(y);
this.z.set(z);
}
createRigidBodyLink(body: AssemblyCSysNode) {
return [
new AlgNumConstraint(Constraints3D.RigidBodyLink4x4, [body, this])
];
}
}

View file

@ -0,0 +1,91 @@
import React, {useContext, useEffect} from 'react';
import {useStream} from "ui/effects";
import {Status} from "ui/components/Status";
import Folder from "ui/components/Folder";
import {Constraints3D} from "../constraints3d";
import {AppContext} from "../../dom/components/AppContext";
import cx from 'classnames';
import {NoIcon} from "../../../sketcher/icons/NoIcon";
import ls from "../../../sketcher/components/ConstraintExplorer.less";
import Fa from "ui/components/Fa";
import {AssemblyConstraintDefinition} from "../assemblyConstraintDefinition";
import {ApplicationContext} from "context";
export function AssemblyView() {
const ctx = useContext(AppContext);
const constraints = useStream(ctx => ctx.assemblyService.constraints$);
const status = useStream(ctx => ctx.assemblyService.status$);
return <div>
<div>
Status: <Status success={status.success} />
</div>
{constraints.map((stage, i) => <Folder key={i} title={'Stage' + (i + 1)}>
{stage.map((constr, j) => <AssemblyConstraintButton key={j} prefix={j + '.'} constraint={constr} />) }
</Folder>)}
</div>
}
export function AssemblyConstraintButton({prefix='', constraint: c, ...props}: {
prefix: string,
constraint: AssemblyConstraintDefinition,
props?: React.HTMLAttributes<HTMLDivElement>
}) {
const ctx: ApplicationContext = useContext(AppContext);
const edit = (constraint) => {
if (constraint.editable) {
//...
}
};
const remove = constr => {
ctx.assemblyService.removeConstraint(constr);
};
const highlight = constr => {
ctx.services.marker.clear();
constr.objects.forEach(id => {
const entity = ctx.cadRegistry.find(id);
if (entity) {
ctx.services.marker.markAdding(entity.TYPE, id);
}
});
};
const withdraw = () => {
ctx.services.marker.clear();
};
useEffect(() => withdraw, [c]);
const schema = Constraints3D[c.typeId];
if (schema === null) {
return <div className='warning-text'>Invalid Constraint {c.typeId} </div>
}
const entities = c.objects.map(ctx.cadRegistry.find);
const invalid = !!entities.find(x => !x);
const Icon = schema.icon || NoIcon;
return <div className={cx(ls.objectItem, invalid&&ls.conflicting)}
onClick={() => schema.constants && edit(c)}
onMouseEnter={() => highlight(c)}
onMouseLeave={() => withdraw()}
{...props}>
<span className={ls.objectIcon}><Icon size={16} /></span>
<span className={ls.objectTag}>
{prefix} {schema.name}
</span>
<span className={ls.removeButton} onClick={() => remove(c)}><Fa icon='times'/></span>
</div>
}

View file

@ -0,0 +1,36 @@
import React, {useContext} from 'react';
import {AppContext} from "../../dom/components/AppContext";
import {useStream} from "ui/effects";
import {Dialog} from "ui/components/Dialog";
import {AssemblyConstraintSchema, Constraints3D} from "../constraints3d";
import {matchAvailableSubjects, MatchIndex, matchSelection} from "../../../sketcher/selectionMatcher";
export function ModellerContextualActions() {
const ctx = useContext(AppContext);
const selection: string[] = useStream(ctx => ctx.streams.selection.all);
if (!selection || selection.length === 0) {
return null;
}
const entities = selection.map(ctx.cadRegistry.find);
const allConstraints = Object.values(Constraints3D) as AssemblyConstraintSchema[];
const availableConstraints = matchAvailableSubjects(entities, allConstraints) as AssemblyConstraintSchema[];
if (availableConstraints.length === 0) {
return null;
}
return <Dialog initRight={50} title='AVAILABLE ACTIONS' onClose={() => {}}>
{availableConstraints.map( schema => <button key={schema.id}
onClick={() => {
const objects = matchSelection(schema.selectionMatcher, new MatchIndex(entities), false);
ctx.assemblyService.addConstraint(schema.id, objects.map(o => o.id));
}}>{schema.name}</button> ) }
</Dialog>;
}

View file

@ -99,11 +99,16 @@ export function activate(ctx: CoreContext) {
return runRequest(request);
}
}
function isEditingHistory() {
const mods = this.modifications$.value;
return mods && mods.pointer !== mods.history.length - 1;
}
ctx.craftService = {
modify, modifyInHistoryAndStep, reset, rebuild, runRequest, runPipeline,
historyTravel: historyTravel(modifications$),
modifications$, models$, update$
modifications$, models$, update$, isEditingHistory
};
// @ts-ignore
@ -256,7 +261,9 @@ interface CraftService {
runRequest(request: OperationRequest): Promise<OperationResult>;
runPipeline(history: OperationRequest[], beginIndex: number, endIndex: number): Promise<void>
runPipeline(history: OperationRequest[], beginIndex: number, endIndex: number): Promise<void>;
isEditingHistory(): boolean;
}
interface HistoryTravel {

View file

@ -1,377 +0,0 @@
import React, {useContext} from 'react';
import {AppContext} from "./AppContext";
import {useStream} from "ui/effects";
import {ApplicationContext} from "context";
import {AlgNumSubSystem} from "../../../sketcher/constr/AlgNumSystem";
import {ParallelConstraintIcon} from "../../../sketcher/icons/constraints/ConstraintIcons";
import {DEG_RAD, makeAngle0_360} from "../../../math/math";
import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints";
import {Param} from "../../../sketcher/shapes/param";
import {COS_FN, Polynomial, POW_1_FN, POW_2_FN, SIN_FN} from "../../../sketcher/constr/polynomial";
import {Matrix3} from "math/l3space";
import CSys from "math/csys";
import Vector from "math/vector";
import {Dialog} from "ui/components/Dialog";
import {MObject} from "../../model/mobject";
import {MBrepFace} from "../../model/mface";
import {solveAssembly} from "../../assembly/assemblySolver";
import {Constraints3D, createAssemblyConstraint} from "../../assembly/constraints3d";
export function ModellerContextualActions({}) {
const ctx = useContext(AppContext);
const faceSelection: string[] = useStream(ctx => ctx.streams.selection.face);
if (faceSelection.length === 0) {
return null;
}
const actions = [];
if (faceSelection.length === 2) {
actions.push(<button key='faceParallel' onClick={() => faceParallel(ctx, faceSelection)}>Face Parallel</button>);
}
return <Dialog initRight={50} title='AVAILABLE ACTIONS' onClose={() => {}}>
{actions}
</Dialog>;
}
const XConstraints3D = {
FaceParallel: {
id: 'FaceParallel',
name: 'FaceParallel',
icon: ParallelConstraintIcon,
defineAssemblyScope: ([face1, face2], cb) => {
cb(face1.assemblyNodes.normal);
cb(face2.assemblyNodes.normal);
},
defineParamsScope: (objects, cb) => {
const [face1W, face2W, csys1W, csys2W] = objects;
const n1 = face1W.normal;
const n2 = face2W.normal;
const [nx1, ny1, nz1] = n1;
const [nx2, ny2, nz2] = n2;
const csysParams1 = csys1W.params;
const [
ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1
] = csysParams1;
const csysParams2 = csys2W.params;
const [
ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2
] = csysParams2;
[
nx1, ny1, nz1, nx2, ny2, nz2,
ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1,
ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2
].forEach(cb);
},
collectPolynomials: (polynomials, params, constants, [face1, face2, csys1, csys2]) => {
const [
nx1, ny1, nz1, nx2, ny2, nz2,
ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1,
ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2
] = params;
polynomials.push(
new Polynomial(1)
.monomial()
.term(nx1, POW_1_FN)
.term(nx2, POW_1_FN)
.monomial()
.term(ny1, POW_1_FN)
.term(ny2, POW_1_FN)
.monomial()
.term(nz1, POW_1_FN)
.term(nz2, POW_1_FN)
);
rigidBodyLink3x3(
[ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1],
csys1.csys,
face1.normal
).forEach(p => polynomials.push(p));
rigidBodyLink3x3(
[ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2],
csys2.csys,
face2.normal
).forEach(p => polynomials.push(p));
polynomials.push(
new Polynomial(-1)
.monomial()
.term(nx1, POW_2_FN)
.monomial()
.term(ny1, POW_2_FN)
.monomial()
.term(nz1, POW_2_FN)
);
polynomials.push(
new Polynomial(-1)
.monomial()
.term(nx2, POW_2_FN)
.monomial()
.term(ny2, POW_2_FN)
.monomial()
.term(nz2, POW_2_FN)
);
}
},
};
function vectorParams(vec) {
const {x, y, z} = vec;
return [new Param(x, 'X'), new Param(y, 'Y'), new Param(z, 'Z')];
}
function csysParams(csys) {
const {x, y, z} = csys.origin;
return [
new Param(x, 'X'),
new Param(y, 'Y'),
new Param(z, 'Z'),
new Param(csys.x.x, 'X'),
new Param(csys.x.y, 'Y'),
new Param(csys.x.z, 'Z'),
new Param(csys.y.x, 'X'),
new Param(csys.y.y, 'Y'),
new Param(csys.y.z, 'Z'),
new Param(csys.z.x, 'X'),
new Param(csys.z.y, 'Y'),
new Param(csys.z.z, 'Z')
];
}
function faceWrapper(face: MBrepFace) {
return {
constraints: new Set(),
normal: vectorParams(face.normal()),
face,
visitParams(cb) {
this.normal.forEach(cb);
}
}
}
function csysWrapper(csys: CSys) {
return {
constraints: new Set(),
params: csysParams(csys),
csys,
visitParams(cb) {
this.params.forEach(cb);
}
}
}
function faceParallel(ctx: ApplicationContext, faceSelection: string[]) {
const [face1, face2] = faceSelection.map(id => ctx.cadRegistry.find(id));
const constraints = [
createAssemblyConstraint(Constraints3D.FaceParallel, [face1, face2])
];
solveAssembly(constraints);
}
function faceParallelLegacy(ctx: ApplicationContext, faceSelection: string[]) {
const [face1, face2] = faceSelection.map(id => ctx.cadRegistry.find(id));
const stage = {};
const objects = [
faceWrapper(face1),
faceWrapper(face2),
csysWrapper(face1.shell.csys),
csysWrapper(face2.shell.csys),
];
objects.forEach(o => o.stage = stage);
stage.objects = objects;
const algNumConstraint = new AlgNumConstraint(XConstraints3D.FaceParallel, objects);
const system = new AlgNumSubSystem(() => 0.001, val => val, stage);
// __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get())))
// __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get())))
system.startTransaction();
system.addConstraint(algNumConstraint);
system.prepare();
system.solveFine();
system.finishTransaction();
__DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get())))
__DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get())))
function applyResults(shell, targetCsysParams, normal) {
const [
ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz
] = targetCsysParams.map(p => p.get());
const targetCsys = new CSys(
new Vector(ox, oy, oz),
new Vector(ix, iy, iz),
new Vector(jx, jy, jz),
new Vector(kx, ky, kz),
);
const basis = [
new Vector(ix, iy, iz),
new Vector(jx, jy, jz),
new Vector(kx, ky, kz),
];
// __DEBUG__.AddCSys(shell.csys);
__DEBUG__.AddCSys(targetCsys);
const tr = shell.csys.inTransformation3x3;
basis.forEach(r => tr._apply(r));
shell.location$.update(csys => {
return targetCsys;
});
// shell.location$.mutate(csys => {
// csys.x = basis[0];
// csys.y = basis[1];
// csys.z = basis[2];
// csys.origin = new Vector(ox, oy, oz)._minus(shell.csys.origin);
// });
}
applyResults(face1.shell, objects[2].params, new Vector().set3(objects[0].normal.map(p => p.get())));
applyResults(face2.shell, objects[3].params, new Vector().set3(objects[1].normal.map(p => p.get())));
}
function rigidBodyLink3x3(csysParams, csys, vector) {
const [ix, iy, iz, jx, jy, jz, kx, ky, kz] = csysParams;
const [x, y, z] = vector;
// const [nStarX, nStarY, nStarZ] = csys.inTransformation3x3.apply3(vector.map(p => p.get()));
const [nStarX, nStarY, nStarZ] = vector.map(p => p.get());
// out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx;
// out.y = this.myx * x + this.myy * y + this.myz * z + this.ty;
// out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz;
return [
new Polynomial(0)
.monomial(-1)
.term(x, POW_1_FN)
.monomial(nStarX)
.term(ix, POW_1_FN)
.monomial(nStarY)
.term(jx, POW_1_FN)
.monomial(nStarZ)
.term(kx, POW_1_FN),
new Polynomial(0)
.monomial(-1)
.term(y, POW_1_FN)
.monomial(nStarX)
.term(iy, POW_1_FN)
.monomial(nStarY)
.term(jy, POW_1_FN)
.monomial(nStarZ)
.term(ky, POW_1_FN),
new Polynomial(0)
.monomial(-1)
.term(z, POW_1_FN)
.monomial(nStarX)
.term(iz, POW_1_FN)
.monomial(nStarY)
.term(jz, POW_1_FN)
.monomial(nStarZ)
.term(kz, POW_1_FN),
new Polynomial(0)
.monomial()
.term(ix, POW_1_FN)
.term(jx, POW_1_FN)
.monomial()
.term(iy, POW_1_FN)
.term(jy, POW_1_FN)
.monomial()
.term(iz, POW_1_FN)
.term(jz, POW_1_FN),
new Polynomial(0)
.monomial()
.term(ix, POW_1_FN)
.term(kx, POW_1_FN)
.monomial()
.term(iy, POW_1_FN)
.term(ky, POW_1_FN)
.monomial()
.term(iz, POW_1_FN)
.term(kz, POW_1_FN),
new Polynomial(0)
.monomial()
.term(jx, POW_1_FN)
.term(kx, POW_1_FN)
.monomial()
.term(jy, POW_1_FN)
.term(ky, POW_1_FN)
.monomial()
.term(jz, POW_1_FN)
.term(kz, POW_1_FN),
new Polynomial(-1)
.monomial()
.term(ix, POW_2_FN)
.monomial()
.term(iy, POW_2_FN)
.monomial()
.term(iz, POW_2_FN),
new Polynomial(-1)
.monomial()
.term(jx, POW_2_FN)
.monomial()
.term(jy, POW_2_FN)
.monomial()
.term(jz, POW_2_FN),
new Polynomial(-1)
.monomial()
.term(kx, POW_2_FN)
.monomial()
.term(ky, POW_2_FN)
.monomial()
.term(kz, POW_2_FN),
]
}

View file

@ -20,7 +20,6 @@ import SketcherOperationWizard from "../../../sketcher/components/SketcherOperat
import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import {ContributedComponents} from "./ContributedComponents";
import {ModellerContextualActions} from "./ModellerContextualActions";
export default class View3d extends React.Component {
@ -59,7 +58,6 @@ export default class View3d extends React.Component {
<WizardManager/>
</div>
<ContributedComponents/>
<Scope><ModellerContextualActions /></Scope>
</div>
<div className={ls.bottomStack}>

View file

@ -37,6 +37,7 @@ import * as DebugPlugin from "../debugPlugin";
import * as ExpressionsPlugin from "../expressions/expressionsPlugin";
import * as PartOperationsPlugin from "../part/partOperationsPlugin";
import * as LocationPlugin from "../location/locationPlugin";
import * as AssemblyPlugin from "../assembly/assemblyPlugin";
export default function startApplication(callback) {
@ -76,6 +77,7 @@ export default function startApplication(callback) {
DebugPlugin,
PartOperationsPlugin,
LocationPlugin,
AssemblyPlugin,
RemotePartsPlugin,
ViewSyncPlugin,
WizardSelectionPlugin

View file

@ -7,7 +7,10 @@ import CSys from 'math/csys';
import {MSketchLoop} from './mloop';
import {ProductionInfo} from './productionInfo';
import {MBrepShell, MShell} from "./mshell";
import {AssemblyUnitVectorNode} from "../assembly/assembly";
import {AssemblyUnitVectorNode} from "../assembly/nodes/assemblyUnitVectorNode";
import {AssemblyScalarNode} from "../assembly/nodes/assemblyScalarNode";
import {AssemblyVectorNode} from "../assembly/nodes/assemblyVectorNode";
import {AssemblyPlaneNode} from "../assembly/nodes/assemblyPlaneNode";
export class MFace extends MObject {
@ -20,7 +23,9 @@ export class MFace extends MObject {
brepFace: any;
assemblyNodes: {
normal: AssemblyUnitVectorNode
// normal: AssemblyUnitVectorNode
plane: AssemblyPlaneNode,
// w: AssemblyScalarNode
};
private _csys: any;
@ -38,15 +43,17 @@ export class MFace extends MObject {
this.sketchLoops = [];
this._csys = csys;
this.assemblyNodes = {
normal: new AssemblyUnitVectorNode(this, () => this.normal())
// normal: new AssemblyUnitVectorNode(this, () => this.normal()),
// w: new AssemblyScalarNode(this, 'W', () => this.depth())
plane: new AssemblyPlaneNode(this, () => this.normal(), () => this.depth())
};
}
normal() {
normal(): Vector {
return this.csys.z;
}
depth() {
depth(): number {
this.evalCSys();
return this.w;
}

View file

@ -5,7 +5,7 @@ import {MVertex} from './mvertex';
import CSys from 'math/csys';
import {Matrix3} from "math/l3space";
import {state, StateStream} from "lstream";
import {AssemblyCSysNode} from "../assembly/assembly";
import {AssemblyCSysNode} from "../assembly/nodes/assemblyCSysNode";
export class MShell extends MObject {

View file

@ -4,6 +4,7 @@ import exportTextData from '../../../../modules/gems/exportTextData';
import {SketchFormat_V3} from "../../sketcher/io";
import {ApplicationContext} from "context";
import {OperationRequest} from "../craft/craftPlugin";
import {AssemblyConstraintDefinition} from "../assembly/assemblyConstraintDefinition";
export function activate(ctx: ApplicationContext) {
@ -202,6 +203,8 @@ export interface ProjectModel {
expressions: string
assembly?: AssemblyConstraintDefinition[][];
}
export interface ModelBundle {

View file

@ -40,7 +40,10 @@ export function initProjectService(ctx: CoreContext, id: string, hints: any) {
function save() {
let data = {
history: ctx.craftService.modifications$.value.history,
expressions: ctx.expressionService.script$.value
expressions: ctx.expressionService.script$.value,
// @ts-ignore we deliberately don't uplift the type to the ApplicationContext in order to be able to use ProjectService in the headless mode
assembly: ctx.assemblyService && ctx.assemblyService.getConstraints()
};
ctx.storageService.set(projectStorageKey(), JSON.stringify(data));
}
@ -65,6 +68,13 @@ export function initProjectService(ctx: CoreContext, id: string, hints: any) {
if (data.history) {
ctx.craftService.reset(data.history);
}
// @ts-ignore we deliberately don't uplift the type to the ApplicationContext in order to be able to use ProjectService in the headless mode
if (data.assembly && ctx.assemblyService) {
// @ts-ignore
ctx.assemblyService.loadConstraints(data.assembly);
}
}
function empty() {

View file

@ -3,6 +3,7 @@ import {state} from 'lstream';
import {addToListInMap} from 'gems/iterables';
import {EMPTY_ARRAY} from '../../../../modules/gems/iterables';
import {DATUM, FACE, SHELL, SKETCH_OBJECT, EDGE, LOOP} from './entites';
import {combine} from "../../../../modules/lstream";
export const SELECTABLE_ENTITIES = [FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL];
@ -11,6 +12,7 @@ export function defineStreams(ctx) {
SELECTABLE_ENTITIES.forEach(entity => {
ctx.streams.selection[entity] = state([]);
});
ctx.streams.selection.all = combine(...Object.values(ctx.streams.selection)).map(selection => [].concat(...selection)).throttle();
}
export function activate(ctx) {

View file

@ -5,6 +5,7 @@ import sketcherUIContrib from './sketcherUIContrib';
import initReassignSketchMode from './reassignSketchMode';
import {Viewer} from "../../sketcher/viewer2d";
import {IO} from "../../sketcher/io";
import {Generator} from "../../sketcher/id-generator";
export function defineStreams(ctx) {
ctx.streams.sketcher = {
@ -67,6 +68,7 @@ export function activate(ctx) {
services.storage.set(ctx.projectService.sketchStorageKey(sketchId), viewer.io.serializeSketch({
expressionsSignature: signature
}));
Generator.resetIDGenerator();
} catch (e) {
console.error(e);
return null;

View file

@ -1,5 +1,5 @@
import constraintActions from "./constraintActions";
import {getDescription, MatchIndex, matchSelection} from "../selectionMatcher";
import {getDescription, matchAvailableSubjects, MatchIndex, matchSelection} from "../selectionMatcher";
import {toast} from "react-toastify";
import operationActions from "./operationActions";
import constraintGlobalActions from "./constraintGlobalActions";
@ -35,19 +35,7 @@ ALL_ACTIONS.forEach(a => index[a.id] = a);
Object.freeze(index);
export function matchAvailableActions(selection) {
let matched = [];
let matchIndex = new MatchIndex(selection);
if (selection.length) {
for (let action of ALL_CONTEXTUAL_ACTIONS) {
if (action.selectionMatcher && matchSelection(action.selectionMatcher, matchIndex, true)) {
matched.push(action);
}
}
}
return matched;
return matchAvailableSubjects(selection, ALL_CONTEXTUAL_ACTIONS);
}
export function getSketcherAction(actionId) {

View file

@ -10,7 +10,7 @@ export function SketchObjectExplorer() {
const [modification, setModification] = useState(0);
const objects = useStream(ctx => ctx.viewer.streams.objects);
const selection = useStream(ctx.viewer.streams.selection);
const selection = useStream(ctx => ctx.viewer.streams.selection);
const ctx = useContext(SketcherAppContext);
if (!objects || !selection) {

View file

@ -36,6 +36,7 @@ import {
import {ISolveStage, SolvableObject} from "./solvableObject";
import {SketchObject} from "../shapes/sketch-object";
import {IconType} from "react-icons";
import {ConstraintAnnotation} from "./constraintAnnotation";
export const ConstraintDefinitions
// : {
@ -966,7 +967,7 @@ export interface ConstraintSchema {
}
};
createAnnotations?: (objects: SolvableObject[], constraintInstance: AlgNumConstraint) => SketchObject[];
createAnnotations?: (objects: SolvableObject[], constraintInstance: AlgNumConstraint) => ConstraintAnnotation<any>[];
defineParamsScope: (object: SolvableObject[], cb: (param: Param) => void) => void;
@ -989,9 +990,9 @@ export class AlgNumConstraint {
schema: ConstraintSchema;
params: Param[];
stage: ISolveStage;
private annotations: SketchObject[];
annotations: ConstraintAnnotation<any>[];
constructor(schema: ConstraintSchema, objects: SolvableObject[], constants?: ConstantsDefinitions, internal?: boolean = false) {
constructor(schema: ConstraintSchema, objects: SolvableObject[], constants?: ConstantsDefinitions, internal: boolean = false) {
this.id = schema.id + ':' + (AlgNumConstraint.Counter ++); // only for debug purposes - not persisted
this.objects = objects;
this.constants = constants;
@ -1038,7 +1039,7 @@ export class AlgNumConstraint {
}
}
write() {
write(): ConstraintSerialization {
return {
typeId: this.schema.id,
objects: this.objects.map(o => o.id),
@ -1048,7 +1049,7 @@ export class AlgNumConstraint {
}
}
static read({typeId, objects, constants, annotations}, index) {
static read({typeId, objects, constants, annotations}: ConstraintSerialization, index: {[key: string]: SolvableObject}) {
const schema = ConstraintDefinitions[typeId];
if (!schema) {
throw "constraint schema " + typeId + " doesn't exist";
@ -1106,3 +1107,12 @@ export class AlgNumConstraint {
this.constants[key] = value + ''; // only string are allowed here
}
}
export interface ConstraintSerialization {
typeId: string;
objects: string[];
constants: ConstantsDefinitions;
stage: number;
annotations?: any
}

View file

@ -626,7 +626,7 @@ class PolynomialResidual {
}
interface SolveStatus {
export interface SolveStatus {
success: boolean;
error: number
error: number;
}

View file

@ -0,0 +1,6 @@
export interface ConstraintAnnotation<T> {
save(): T;
load(data: T);
}

View file

@ -1,3 +1,20 @@
export function matchAvailableSubjects(selection, subjects) {
let matched = [];
let matchIndex = new MatchIndex(selection);
if (selection.length) {
for (let action of subjects) {
if (action.selectionMatcher && matchSelection(action.selectionMatcher, matchIndex, true)) {
matched.push(action);
}
}
}
return matched;
}
export function matchSelection(definition, matchIndex, fast) {
const selection = matchIndex.selection;
if (definition.selector === 'function') {
@ -12,7 +29,7 @@ export function matchSelection(definition, matchIndex, fast) {
let hit = false;
for (let constructor of types) {
if (constructor.prototype._class === obj._class) {
if (typeToString(constructor) === obj.TYPE) {
hit = true;
break;
}
@ -70,7 +87,7 @@ export function getDescription(definition) {
}
function stringifyTypes(types, minQuantity) {
return types.map(t => t.prototype.TYPE + (minQuantity > 1 ? 's' : '')).join(' or ');
return types.map(t => typeToString(t) + (minQuantity > 1 ? 's' : '')).join(' or ');
}
export class MatchIndex {
@ -82,13 +99,13 @@ export class MatchIndex {
constructor(selection) {
this.selection = selection;
selection.forEach(obj => {
let info = this.typeMap.get(obj._class);
let info = this.typeMap.get(obj.TYPE);
if (!info) {
info = {
hits: 0,
objects: []
};
this.typeMap.set(obj._class, info);
this.typeMap.set(obj.TYPE, info);
}
info.objects.push(obj);
})
@ -102,7 +119,7 @@ export class MatchIndex {
mark(types, quantity) {
for (let type of types) {
const info = this.typeMap.get(type.prototype._class);
const info = this.typeMap.get(typeToString(type));
if (!info) {
continue;
}
@ -123,4 +140,12 @@ export class MatchIndex {
return this.selection.length === this.overallHits;
}
}
function typeToString(type) {
if (typeof type === 'string') {
return type;
} else {
return type.prototype.TYPE;
}
}

View file

@ -1,7 +1,11 @@
import {AngleBetweenDimension, DiameterDimension, LinearDimension} from "../dim";
import {Styles} from "../../styles";
import {ConstraintAnnotation} from "../../constr/constraintAnnotation";
import {AlgNumConstraint} from "../../constr/ANConstraints";
export class AngleBetweenAnnotation extends AngleBetweenDimension {
export class AngleBetweenAnnotation extends AngleBetweenDimension implements ConstraintAnnotation<{offset: number}> {
constraint: AlgNumConstraint;
constructor(a, b, constraint) {
super(a, b);
@ -27,7 +31,9 @@ AngleBetweenAnnotation.prototype.TYPE = 'AngleBetweenAnnotation';
AngleBetweenAnnotation.prototype._class = 'TCAD.TWO.AngleBetweenAnnotation';
export class AngleAbsoluteAnnotation extends AngleBetweenDimension {
export class AngleAbsoluteAnnotation extends AngleBetweenDimension implements ConstraintAnnotation<{offset: number}> {
constraint: AlgNumConstraint;
constructor(segment, constraint) {
super({
@ -90,7 +96,9 @@ export class AngleAbsoluteAnnotation extends AngleBetweenDimension {
AngleAbsoluteAnnotation.prototype._class = 'AngleAbsoluteAnnotation';
export class LengthAnnotation extends LinearDimension {
export class LengthAnnotation extends LinearDimension implements ConstraintAnnotation<{offset: number}> {
constraint: AlgNumConstraint;
constructor(segment, constraint) {
super(segment.a, segment.b);
@ -116,7 +124,9 @@ LengthAnnotation.prototype.TYPE = 'LengthAnnotation';
LengthAnnotation.prototype._class = 'TCAD.TWO.LengthAnnotation';
export class RadiusLengthAnnotation extends DiameterDimension {
export class RadiusLengthAnnotation extends DiameterDimension implements ConstraintAnnotation<{angle: number}> {
constraint: AlgNumConstraint;
constructor(obj, constraint) {
super(obj);

View file

@ -7,6 +7,7 @@ import {TextHelper} from "./textHelper";
import {isInstanceOf} from "../actions/matchUtils";
import {Arc} from "./arc";
import {SketchObject} from "./sketch-object";
import {ConstraintAnnotation} from "../constr/constraintAnnotation";
const ARROW_W_PX = 15;
const ARROW_H_PX = 4;

View file

@ -19,6 +19,7 @@ import {Styles} from './styles';
import {Dimension} from "./shapes/dim";
import {GroundObjectsGeneratorSchema} from "./generators/groundObjectsGenerator";
import {SketchGenerator} from "./generators/sketchGenerator";
import {Generator} from "./id-generator";
export class Viewer {
@ -136,6 +137,7 @@ export class Viewer {
window.removeEventListener('resize', this.onWindowResize, false);
this.canvas = null;
this.toolManager.dispose();
Generator.resetIDGenerator();
};
isDisposed() {