diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js
index f8524154..9c73e50e 100644
--- a/web/app/cad/init/startApplication.js
+++ b/web/app/cad/init/startApplication.js
@@ -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
diff --git a/web/app/cad/model/mface.ts b/web/app/cad/model/mface.ts
index fc9bb0c4..dbd4eb35 100644
--- a/web/app/cad/model/mface.ts
+++ b/web/app/cad/model/mface.ts
@@ -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;
}
diff --git a/web/app/cad/model/mshell.ts b/web/app/cad/model/mshell.ts
index 76362c9c..7461e17f 100644
--- a/web/app/cad/model/mshell.ts
+++ b/web/app/cad/model/mshell.ts
@@ -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 {
diff --git a/web/app/cad/projectManager/projectManagerPlugin.ts b/web/app/cad/projectManager/projectManagerPlugin.ts
index af900553..74ea6f53 100644
--- a/web/app/cad/projectManager/projectManagerPlugin.ts
+++ b/web/app/cad/projectManager/projectManagerPlugin.ts
@@ -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 {
diff --git a/web/app/cad/projectPlugin.ts b/web/app/cad/projectPlugin.ts
index 1eae6b7f..f7bc994a 100644
--- a/web/app/cad/projectPlugin.ts
+++ b/web/app/cad/projectPlugin.ts
@@ -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() {
diff --git a/web/app/cad/scene/entityContextPlugin.js b/web/app/cad/scene/entityContextPlugin.js
index 7e7b780d..ed0b47dc 100644
--- a/web/app/cad/scene/entityContextPlugin.js
+++ b/web/app/cad/scene/entityContextPlugin.js
@@ -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) {
diff --git a/web/app/cad/sketch/sketcherPlugin.ts b/web/app/cad/sketch/sketcherPlugin.ts
index a3df08a7..ae748e65 100644
--- a/web/app/cad/sketch/sketcherPlugin.ts
+++ b/web/app/cad/sketch/sketcherPlugin.ts
@@ -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;
diff --git a/web/app/sketcher/actions/index.js b/web/app/sketcher/actions/index.js
index f03044d0..6be3a3a9 100644
--- a/web/app/sketcher/actions/index.js
+++ b/web/app/sketcher/actions/index.js
@@ -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) {
diff --git a/web/app/sketcher/components/SketchObjectExplorer.jsx b/web/app/sketcher/components/SketchObjectExplorer.jsx
index 4c59deb0..82ecd088 100644
--- a/web/app/sketcher/components/SketchObjectExplorer.jsx
+++ b/web/app/sketcher/components/SketchObjectExplorer.jsx
@@ -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) {
diff --git a/web/app/sketcher/constr/ANConstraints.ts b/web/app/sketcher/constr/ANConstraints.ts
index 25325956..fa61cf6e 100644
--- a/web/app/sketcher/constr/ANConstraints.ts
+++ b/web/app/sketcher/constr/ANConstraints.ts
@@ -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
[];
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[];
- 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
+}
\ No newline at end of file
diff --git a/web/app/sketcher/constr/AlgNumSystem.ts b/web/app/sketcher/constr/AlgNumSystem.ts
index bb31e3a6..4e8478c7 100644
--- a/web/app/sketcher/constr/AlgNumSystem.ts
+++ b/web/app/sketcher/constr/AlgNumSystem.ts
@@ -626,7 +626,7 @@ class PolynomialResidual {
}
-interface SolveStatus {
+export interface SolveStatus {
success: boolean;
- error: number
+ error: number;
}
\ No newline at end of file
diff --git a/web/app/sketcher/constr/constraintAnnotation.ts b/web/app/sketcher/constr/constraintAnnotation.ts
new file mode 100644
index 00000000..1a89311c
--- /dev/null
+++ b/web/app/sketcher/constr/constraintAnnotation.ts
@@ -0,0 +1,6 @@
+export interface ConstraintAnnotation {
+
+ save(): T;
+
+ load(data: T);
+}
\ No newline at end of file
diff --git a/web/app/sketcher/selectionMatcher.js b/web/app/sketcher/selectionMatcher.js
index f26ea139..bd9f84d1 100644
--- a/web/app/sketcher/selectionMatcher.js
+++ b/web/app/sketcher/selectionMatcher.js
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/web/app/sketcher/shapes/annotations/angleAnnotation.js b/web/app/sketcher/shapes/annotations/angleAnnotation.ts
similarity index 81%
rename from web/app/sketcher/shapes/annotations/angleAnnotation.js
rename to web/app/sketcher/shapes/annotations/angleAnnotation.ts
index 33c34f8f..bf215e8c 100644
--- a/web/app/sketcher/shapes/annotations/angleAnnotation.js
+++ b/web/app/sketcher/shapes/annotations/angleAnnotation.ts
@@ -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);
diff --git a/web/app/sketcher/shapes/dim.js b/web/app/sketcher/shapes/dim.js
index 550aae65..38c34e8a 100644
--- a/web/app/sketcher/shapes/dim.js
+++ b/web/app/sketcher/shapes/dim.js
@@ -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;
diff --git a/web/app/sketcher/viewer2d.ts b/web/app/sketcher/viewer2d.ts
index 621a7c5f..ea3ba761 100644
--- a/web/app/sketcher/viewer2d.ts
+++ b/web/app/sketcher/viewer2d.ts
@@ -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() {