From 801480c5f42c3cfd6aaaa8d36f59eecc472643c8 Mon Sep 17 00:00:00 2001 From: "Val Erastov (xibyte)" Date: Thu, 2 Jul 2020 23:45:53 -0700 Subject: [PATCH] basic DOFs for assemblies --- modules/gems/{traverse.js => traverse.ts} | 13 +- modules/math/l3space.ts | 78 ++- tsconfig.json | 3 +- web/app/brep/geom/impl/{line.js => line.ts} | 23 +- web/app/brep/geom/impl/{plane.js => plane.ts} | 60 +- web/app/cad/assembly/assembly.ts | 48 -- web/app/cad/assembly/assemblyConstraint.ts | 56 ++ .../assembly/assemblyConstraintDefinition.ts | 10 - web/app/cad/assembly/assemblyPlugin.ts | 8 +- web/app/cad/assembly/assemblySchemas.ts | 10 + web/app/cad/assembly/assemblySolver.ts | 370 +++++------ .../assembly/constraints/faceTouchAlign.ts | 40 ++ web/app/cad/assembly/constraints3d.ts | 615 ------------------ web/app/cad/assembly/dof/PPDOF.ts | 93 +++ web/app/cad/assembly/dof/PPPPDOF.ts | 31 + web/app/cad/assembly/dof/assemblyDOF.ts | 23 + web/app/cad/assembly/dof/conflictDOF.ts | 33 + web/app/cad/assembly/dof/sixDOF.ts | 39 ++ .../cad/assembly/nodes/assemblyCSysNode.ts | 85 --- .../assembly/nodes/assemblyLocationNode.ts | 81 --- .../assembly/nodes/assemblyOrientationNode.ts | 61 -- .../cad/assembly/nodes/assemblyPlaneNode.ts | 68 -- .../cad/assembly/nodes/assemblyScalarNode.ts | 24 - .../assembly/nodes/assemblyTranslationNode.ts | 40 -- .../assembly/nodes/assemblyUnitVectorNode.ts | 45 -- .../cad/assembly/nodes/assemblyVectorNode.ts | 40 -- web/app/cad/assembly/ui/AssemblyView.tsx | 12 +- .../assembly/ui/ModellerContextualActions.tsx | 5 +- .../cad/assembly/ui/StepByStepSimulation.tsx | 7 +- web/app/cad/craft/cadRegistryPlugin.ts | 7 +- web/app/cad/location/LocationDialog.tsx | 12 +- web/app/cad/model/mdatum.ts | 3 +- web/app/cad/model/mface.ts | 46 +- web/app/cad/model/mobject.ts | 5 + web/app/cad/model/mshell.ts | 23 +- ...electionMatcher.js => selectionMatcher.ts} | 12 + web/app/sketcher/shapes/sketch-object.ts | 2 +- 37 files changed, 721 insertions(+), 1410 deletions(-) rename modules/gems/{traverse.js => traverse.ts} (57%) rename web/app/brep/geom/impl/{line.js => line.ts} (75%) rename web/app/brep/geom/impl/{plane.js => plane.ts} (65%) delete mode 100644 web/app/cad/assembly/assembly.ts create mode 100644 web/app/cad/assembly/assemblyConstraint.ts delete mode 100644 web/app/cad/assembly/assemblyConstraintDefinition.ts create mode 100644 web/app/cad/assembly/assemblySchemas.ts create mode 100644 web/app/cad/assembly/constraints/faceTouchAlign.ts create mode 100644 web/app/cad/assembly/dof/PPDOF.ts create mode 100644 web/app/cad/assembly/dof/PPPPDOF.ts create mode 100644 web/app/cad/assembly/dof/assemblyDOF.ts create mode 100644 web/app/cad/assembly/dof/conflictDOF.ts create mode 100644 web/app/cad/assembly/dof/sixDOF.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyCSysNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyLocationNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyOrientationNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyPlaneNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyScalarNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyTranslationNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyUnitVectorNode.ts delete mode 100644 web/app/cad/assembly/nodes/assemblyVectorNode.ts rename web/app/sketcher/{selectionMatcher.js => selectionMatcher.ts} (96%) diff --git a/modules/gems/traverse.js b/modules/gems/traverse.ts similarity index 57% rename from modules/gems/traverse.js rename to modules/gems/traverse.ts index 51eb995d..c038d724 100644 --- a/modules/gems/traverse.js +++ b/modules/gems/traverse.ts @@ -1,7 +1,9 @@ +export function dfs(node:T, + children: (node: T, consumer: (node: T) => void) => void, + callback: (node) => any): boolean { + const visited = new Set(); -export function dfs(node, children, callback) { - const visited = new Set(); const stack = []; stack.push(node); while (stack.length) { @@ -17,8 +19,11 @@ export function dfs(node, children, callback) { } } -export function bfs(node, children, callback) { - const visited = new Set(); +export function bfs(node:T, + children: (node: T, consumer: (node: T) => void) => void, + callback: (node) => any): boolean { + + const visited = new Set(); const queue = []; queue.unshift(node); while (queue.length) { diff --git a/modules/math/l3space.ts b/modules/math/l3space.ts index 5e6e5a33..878a85d5 100644 --- a/modules/math/l3space.ts +++ b/modules/math/l3space.ts @@ -109,6 +109,13 @@ export class Matrix3 { return this; }; + translateVec({x, y, z}: Vector): Matrix3 { + this.tx += x; + this.ty += y; + this.tz += z; + return this; + }; + set3( mxx: number, mxy: number, mxz: number, myx: number, myy: number, myz: number, @@ -263,6 +270,53 @@ export class Matrix3 { return m; }; + combine3x3(transform: Matrix3, out?: Matrix3): Matrix3 { + var txx = transform.mxx; + var txy = transform.mxy; + var txz = transform.mxz; + + var tyx = transform.myx; + var tyy = transform.myy; + var tyz = transform.myz; + + var tzx = transform.mzx; + var tzy = transform.mzy; + var tzz = transform.mzz; + + + var m = out || new Matrix3(); + m.mxx = (this.mxx * txx + this.mxy * tyx + this.mxz * tzx); + m.mxy = (this.mxx * txy + this.mxy * tyy + this.mxz * tzy); + m.mxz = (this.mxx * txz + this.mxy * tyz + this.mxz * tzz); + + m.myx = (this.myx * txx + this.myy * tyx + this.myz * tzx); + m.myy = (this.myx * txy + this.myy * tyy + this.myz * tzy); + m.myz = (this.myx * txz + this.myy * tyz + this.myz * tzz); + + m.mzx = (this.mzx * txx + this.mzy * tyx + this.mzz * tzx); + m.mzy = (this.mzx * txy + this.mzy * tyy + this.mzz * tzy); + m.mzz = (this.mzx * txz + this.mzy * tyz + this.mzz * tzz); + + + return m; + }; + + __applyNoTranslation(vector: Vector, out: Vector): Vector { + let x = vector.x; + let y = vector.y; + let z = vector.z; + out.x = this.mxx * x + this.mxy * y + this.mxz * z; + out.y = this.myx * x + this.myy * y + this.myz * z; + out.z = this.mzx * x + this.mzy * y + this.mzz * z; + return out; + }; + + _applyNoTranslation(vector: Vector): Vector { + return this.__applyNoTranslation(vector, vector); + }; + + applyNoTranslation = vector => this.__applyNoTranslation(vector, new Vector()); + _apply(vector: Vector): Vector { return this.__apply(vector, vector); }; @@ -308,8 +362,23 @@ export class Matrix3 { }; static rotateMatrix(angle: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 { - var sin = Math.sin(angle); - var cos = Math.cos(angle); + const sin = Math.sin(angle); + const cos = Math.cos(angle); + return Matrix3.rotationMatrix(cos, sin, axis, pivot, matrix); + } + + static rotationFromVectorToVector(from: Vector, to: Vector, pivot: Vector, matrix: Matrix3): Matrix3 { + + const axis = from.cross(to); + + const cos = from.dot(to); + const sin = axis.length(); + + return Matrix3.rotationMatrix(cos, sin, axis, pivot, matrix); + + } + + static rotationMatrix(cos: number, sin: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 { var axisX, axisY, axisZ; var m = matrix || new Matrix3(); @@ -360,10 +429,11 @@ export class Matrix3 { this.tz = tz; return this; } + } -function BasisForPlane(normal: Vector, alignY: Vector = AXIS.Y, alignZ: Vector = AXIS.Z): [number, number, Vector] { +function BasisForPlane(normal: Vector, alignY: Vector = AXIS.Y, alignZ: Vector = AXIS.Z): [Vector, Vector, Vector] { let alignPlane, x, y; if (Math.abs(normal.dot(alignY)) < 0.5) { alignPlane = normal.cross(alignY); @@ -375,4 +445,6 @@ function BasisForPlane(normal: Vector, alignY: Vector = AXIS.Y, alignZ: Vector = return [x, y, normal]; } +export const IDENTITY_MATRIX = Object.freeze(new Matrix3()); + export {ORIGIN, IDENTITY_BASIS, AXIS, BasisForPlane}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c768733e..1c2c3a9e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,9 +6,10 @@ "esModuleInterop": true, "allowJs": true, "allowSyntheticDefaultImports": true, - "target": "ES5", + "target": "ES2015", "baseUrl": ".", "downlevelIteration": true, + "module": "commonJS", "paths": { "*": [ "modules/*", diff --git a/web/app/brep/geom/impl/line.js b/web/app/brep/geom/impl/line.ts similarity index 75% rename from web/app/brep/geom/impl/line.js rename to web/app/brep/geom/impl/line.ts index 5a38259d..e89b6e37 100644 --- a/web/app/brep/geom/impl/line.js +++ b/web/app/brep/geom/impl/line.ts @@ -1,11 +1,19 @@ +import Vector from "math/vector"; export class Line { + p0: Vector; + v: Vector; + + private _pointsCache: Map; + + isLine: boolean; + + static fromTwoPlanesIntersection: (plane1, plane2) => Line; + static fromSegment: (a, b) => Line; constructor(p0, v) { - throw 'only nurbs for now' this.p0 = p0; this.v = v; - this._pointsCache = new Map(); } intersectSurface(surface) { @@ -13,7 +21,8 @@ export class Line { const s0 = surface.normal.multiply(surface.w); return surface.normal.dot(s0.minus(this.p0)) / surface.normal.dot(this.v); // 4.7.4 } else { - return super.intersectSurface(surface); + throw 'unsupported'; + // return super.intersectSurface(surface); } } @@ -22,7 +31,8 @@ export class Line { const otherNormal = surface.normal.cross(curve.v)._normalize(); return otherNormal.dot(curve.p0.minus(this.p0)) / otherNormal.dot(this.v); // (4.8.3) } - return super.intersectCurve(curve, surface); + throw 'unsupported'; + // return super.intersectCurve(curve, surface); } point(t) { @@ -34,6 +44,9 @@ export class Line { } pointOfSurfaceIntersection(surface) { + if (!this._pointsCache) { + this._pointsCache = new Map(); + } let point = this._pointsCache.get(surface); if (!point) { const t = this.intersectSurface(surface); @@ -55,7 +68,7 @@ export class Line { Line.prototype.isLine = true; -Line.fromTwoPlanesIntersection = function(plane1, plane2) { +Line.fromTwoPlanesIntersection = function(plane1, plane2): Line { const n1 = plane1.normal; const n2 = plane2.normal; const v = n1.cross(n2)._normalize(); diff --git a/web/app/brep/geom/impl/plane.js b/web/app/brep/geom/impl/plane.ts similarity index 65% rename from web/app/brep/geom/impl/plane.js rename to web/app/brep/geom/impl/plane.ts index 826b9039..fa1e2c56 100644 --- a/web/app/brep/geom/impl/plane.js +++ b/web/app/brep/geom/impl/plane.ts @@ -3,7 +3,18 @@ import {Line} from './line'; import {AXIS, BasisForPlane, Matrix3} from '../../../../../modules/math/l3space'; import {eqTol, veq} from '../tolerance'; + export class Plane { + normal: any; + w: any; + #basis: any; + #_2dTr: any; + #_3dTr: any; + #parametricForm: any; + + static XY = new Plane(AXIS.Z, 0); + static XZ = new Plane(AXIS.Y, 0); + static YZ = new Plane(AXIS.X, 0); constructor(normal, w) { this.normal = normal; @@ -15,14 +26,14 @@ export class Plane { } basis() { - if (!this._basis) { - this._basis = this.calculateBasis(); + if (!this.#basis) { + this.#basis = this.calculateBasis(); } - return this._basis; + return this.#basis; } intersectForSameClass(other) { - return new Line.fromTwoPlanesIntersection(this, other); + return Line.fromTwoPlanesIntersection(this, other); } translate(vector) { @@ -34,21 +45,21 @@ export class Plane { } get2DTransformation() { - if (!this.__2dTr) { - this.__2dTr = this.get3DTransformation().invert(); + if (!this.#_2dTr) { + this.#_2dTr = this.get3DTransformation().invert(); } - return this.__2dTr; + return this.#_2dTr; } get3DTransformation() { - if (!this.__3dTr) { + if (!this.#_3dTr) { const basis = new Matrix3().setBasis(this.basis()); const translate = new Matrix3(); translate.tz = this.w; - this.__3dTr = basis.combine(translate); -// this.__3dTr.tz = this.w; + this.#_3dTr = basis.combine(translate); +// this.#_3dTr.tz = this.w; } - return this.__3dTr; + return this.#_3dTr; } coplanarUnsigned(other) { @@ -61,11 +72,11 @@ export class Plane { } toParametricForm() { - if (!this.__parametricForm) { - const basis = BasisForPlane(this.normal); - this.__parametricForm = new ParametricPlane(this.normal.multiply(this.w), basis.x, basis.y); + if (!this.#parametricForm) { + const [x, y, z] = BasisForPlane(this.normal); + this.#parametricForm = new ParametricPlane(this.normal.multiply(this.w), x, y); } - return this.__parametricForm; + return this.#parametricForm; } toUV(point) { @@ -96,12 +107,12 @@ export class Plane { Plane.prototype.TYPE = 'plane'; Plane.prototype.isPlane = true; -Plane.XY = new Plane(AXIS.Z, 0); -Plane.XZ = new Plane(AXIS.Y, 0); -Plane.YZ = new Plane(AXIS.X, 0); - class ParametricPlane { + r0: any; + r1: any; + r2: any; + constructor(r0, r1, r2) { this.r0 = r0; this.r1 = r1; @@ -112,3 +123,14 @@ class ParametricPlane { return this.r0 + this.r1.multiply(u) + this.r2.multiply(v); } } + + +declare module './plane' { + interface Plane { + + TYPE: string; + + isPlane: boolean; + + } +} \ No newline at end of file diff --git a/web/app/cad/assembly/assembly.ts b/web/app/cad/assembly/assembly.ts deleted file mode 100644 index bb42ab16..00000000 --- a/web/app/cad/assembly/assembly.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {MObject} from "../model/mobject"; -import {Param} from "../../sketcher/shapes/param"; -import {ISolveStage, SolvableObject} from "../../sketcher/constr/solvableObject"; -import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints"; -import {AssemblyCSysNode} from "./nodes/assemblyCSysNode"; -import {AssemblyOrientationNode} from "./nodes/assemblyOrientationNode"; -import {Constraints3D} from "./constraints3d"; -import {AssemblyLocationNode} from "./nodes/assemblyLocationNode"; - -export abstract class AssemblyNode implements SolvableObject { - - constraints: Set = new Set(); - - model: MObject; - - stage: ISolveStage; - - id: string; - - protected constructor(model: MObject) { - this.model = model; - this.id = 'assembly-node:' + model.id; - } - - abstract visitParams(cb); - - abstract reset(); - - createConsistencyConstraints(): AlgNumConstraint[] { - return []; - } - - createOrientationRelationship(location: AssemblyLocationNode): AlgNumConstraint[] { - return []; - } - - createTranslationRelationship(location: AssemblyLocationNode): AlgNumConstraint[] { - return []; - } - - get params(): Param[] { - const paramArray = []; - this.visitParams(p => paramArray.push(p)); - return paramArray; - } - -} - diff --git a/web/app/cad/assembly/assemblyConstraint.ts b/web/app/cad/assembly/assemblyConstraint.ts new file mode 100644 index 00000000..39110de3 --- /dev/null +++ b/web/app/cad/assembly/assemblyConstraint.ts @@ -0,0 +1,56 @@ +import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints"; +import {IconType} from "react-icons"; +import {MObject} from "../model/mobject"; +import {AssemblyDOF} from "./dof/assemblyDOF"; +import { MShell } from "../model/mshell"; + +export interface AssemblyConstraintDefinition { + + typeId: string; + + objects: string[]; + + constants: ConstantsDefinitions + +} + +export interface AssemblyConstraintSchema { + + constants?: ConstantsDefinitions; + + id: string, + name: string, + icon?: IconType, + + selectionMatcher?: { + selector: string, + types: any[], + minQuantity: number + }; + + implementation: { new(schema: AssemblyConstraintSchema, fixedPart: MObject, movingPart: MObject, objects: MObject[]): AssemblyConstraint }; +} + +export abstract class AssemblyConstraint { + + schema: AssemblyConstraintSchema; + + fixedPart: MShell; + movingPart: MShell; + + objects: MObject[]; + + constants: ConstantsDefinitions = {}; + + protected constructor(schema: AssemblyConstraintSchema, fixedPart: MShell, movingPart: MShell, objects: MObject[]) { + this.schema = schema; + this.fixedPart = fixedPart; + this.movingPart = movingPart; + this.objects = objects; + } + + abstract apply(dof: AssemblyDOF); + +} + + diff --git a/web/app/cad/assembly/assemblyConstraintDefinition.ts b/web/app/cad/assembly/assemblyConstraintDefinition.ts deleted file mode 100644 index ce80390b..00000000 --- a/web/app/cad/assembly/assemblyConstraintDefinition.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints"; - -export interface AssemblyConstraintDefinition { - - typeId: string; - - objects: string[]; - - constants: ConstantsDefinitions -} diff --git a/web/app/cad/assembly/assemblyPlugin.ts b/web/app/cad/assembly/assemblyPlugin.ts index 54b65fb9..b6fe0bed 100644 --- a/web/app/cad/assembly/assemblyPlugin.ts +++ b/web/app/cad/assembly/assemblyPlugin.ts @@ -1,13 +1,15 @@ import {ApplicationContext} from "context"; import {ModellerContextualActions} from "./ui/ModellerContextualActions"; import {state, StateStream} from "lstream"; -import {AssemblyConstraintDefinition} from "./assemblyConstraintDefinition"; + import {AssemblyProcess, launchAssembly} from "./assemblySolver"; import {SolveStatus} from "../../sketcher/constr/AlgNumSystem"; import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints"; import {AssemblyView} from "./ui/AssemblyView"; import {IoMdConstruct} from "react-icons/io"; -import {AssemblyConstraints, Constraints3D} from "./constraints3d"; +import {AssemblyConstraintDefinition} from "./assemblyConstraint"; +import {AssemblyConstraintsSchemas} from "./assemblySchemas"; + export function activate(ctx: ApplicationContext) { @@ -20,7 +22,7 @@ export function activate(ctx: ApplicationContext) { function loadConstraints(inData: AssemblyConstraintDefinition[]): void { inData = inData.filter(constr => { - const shouldBeFiltered = !AssemblyConstraints[constr.typeId]; + const shouldBeFiltered = !AssemblyConstraintsSchemas[constr.typeId]; if (shouldBeFiltered) { console.log('Unknown constraint ' + constr.typeId + ' will be skipped'); } diff --git a/web/app/cad/assembly/assemblySchemas.ts b/web/app/cad/assembly/assemblySchemas.ts new file mode 100644 index 00000000..fde9e1e5 --- /dev/null +++ b/web/app/cad/assembly/assemblySchemas.ts @@ -0,0 +1,10 @@ +import {AssemblyConstraintSchema} from "./assemblyConstraint"; +import {FaceTouchAlign} from "./constraints/faceTouchAlign"; + +export const AssemblyConstraintsSchemas: { + [typeId: string]: AssemblyConstraintSchema +} = { + + FaceTouchAlign + +}; diff --git a/web/app/cad/assembly/assemblySolver.ts b/web/app/cad/assembly/assemblySolver.ts index 3c5d07d4..dfd666fa 100644 --- a/web/app/cad/assembly/assemblySolver.ts +++ b/web/app/cad/assembly/assemblySolver.ts @@ -1,33 +1,30 @@ -import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints"; -import {AlgNumSubSystem, SolveStatus} from "../../sketcher/constr/AlgNumSystem"; -import Vector from "math/vector"; -import CSys from "math/csys"; -import {AssemblyNode} from "./assembly"; -import {ISolveStage} from "../../sketcher/constr/solvableObject"; +import {SolveStatus} from "../../sketcher/constr/AlgNumSystem"; import {MShell} from "../model/mshell"; -import {AssemblyCSysNode} from "./nodes/assemblyCSysNode"; -import {AssemblyConstraints, AssemblyConstraintSchema, Constraints3D} from "./constraints3d"; -import {AssemblyConstraintDefinition} from "./assemblyConstraintDefinition"; import {MObject} from "../model/mobject"; import {CadRegistry} from "../craft/cadRegistryPlugin"; +import {Matrix3} from "math/l3space"; +import {AssemblyConstraint, AssemblyConstraintDefinition} from "./assemblyConstraint"; +import {AssemblyConstraintsSchemas} from "./assemblySchemas"; +import {dfs} from "gems/traverse"; +import {SixDOF} from "./dof/sixDOF"; +import {AssemblyDOF} from "./dof/assemblyDOF"; -export class RigidBody { +declare module '../model/mshell' { + + interface MShell { - model: MObject; - relationships = new Map(); + assemblyDOF: AssemblyDOF; - reset() { - this.model.traverse(m => { - if (m.assemblyNodes) { - Object.values(m.assemblyNodes).forEach((node: AssemblyNode) => node.reset()); - } - }) } + } -export class AssemblyConstraint { - objects: MObject[] = []; - schema: AssemblyConstraintSchema; +export interface RigidBody { + + model: MShell; + + constraints; + } export class AssemblyProcess { @@ -39,37 +36,31 @@ export class AssemblyProcess { error: 0 }; errorStep = null; + cadRegistry: CadRegistry; constructor(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]) { + this.cadRegistry = cadRegistry; this.queue = buildAssemblyQueue(cadRegistry, constraintDefs) } + begin() { + this.cadRegistry.getAllShells().forEach(s => { + s.location$.mutate(l => l.reset()); + s.assemblyDOF = new SixDOF(); + }); + } + step() { const body = this.queue.pop(); - const constraints = []; - body.relationships.forEach((overConstraints, bodyBuddy) => { - if (this.solved.has(bodyBuddy)) { - overConstraints.forEach(c => constraints.push(c)); - } - }); - body.reset(); - - this.solveStatus = solve(constraints, body.model, true); + this.solveStatus = solve(body.constraints, body.model, body.model.location); if (!this.solveStatus.success) { this.errorStep = body.model.id; - console.log("Assembly system haven't been solved at the orientation step"); + console.log("Assembly system hasn't been solved at the orientation step"); return; } - // this.solveStatus = solve(constraints, body.model, false); - // if (!this.solveStatus.success) { - // console.log("Assembly system haven't been solved at the translation step"); - // this.errorStep = body.model.id; - // return; - // } - - applyLocation(body.model as MShell); + (body.model as MShell).location$.next(body.model.location); this.solved.add(body); } @@ -80,73 +71,145 @@ export class AssemblyProcess { } - function buildAssemblyQueue(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]): RigidBody[] { const constraints: AssemblyConstraint[] = []; + const graph: Map = new Map(); + function assignConstraint(obj: MObject, constr: AssemblyConstraint) { + let constrs = graph.get(obj); + if (!constrs) { + constrs = []; + graph.set(obj, constrs) + } + constrs.push(constr); + } constraintDefs.forEach(def => { - const schema = AssemblyConstraints[def.typeId]; + const schema = AssemblyConstraintsSchemas[def.typeId]; if (!schema) { console.error('reference to nonexistent constraint ' + def.typeId); return null; } - const constraint = new AssemblyConstraint(); - constraint.schema = schema; - const objects = []; + + const objects: MObject[] = []; + let movingPart: MObject = null; + let fixedPart: MObject = null; + for (const id of def.objects) { const modelObject = cadRegistry.find(id); if (!modelObject) { console.warn('skipping constraint referring to nonexistent object ' + id); return null; } - constraint.objects.push(modelObject); objects.push(modelObject); - } - constraints.push(constraint); - }); - - const bodies = new Map(); - function body(obj: MObject) { - let rigidBody = bodies.get(obj); - if (!rigidBody) { - rigidBody = new RigidBody(); - rigidBody.model = obj; - bodies.set(obj, rigidBody); - } - return rigidBody; - } - - function link(a: MObject, b: MObject, constr: AssemblyConstraint) { - const rigidBodyA = body(a); - const rigidBodyB = body(b); - - let arr = rigidBodyA.relationships.get(rigidBodyB); - if (!arr) { - arr = []; - rigidBodyA.relationships.set(rigidBodyB, arr); - } - arr.push(constr) - } - - constraints.forEach(constr => { - const roots = new Set(); - constr.objects.forEach(o => roots.add(o.root)); - const arr: MObject[] = Array.from(roots); - for (let i = 0; i < arr.length; i++) { - for (let j = i+1; j < arr.length; j++) { - link(arr[i], arr[j], constr); - link(arr[j], arr[i], constr); + if (movingPart === null) { + movingPart = modelObject.root; + } else if (fixedPart === null) { + if (modelObject.root !== movingPart) { + fixedPart = modelObject.root; + } + } else { + console.error('constraint may only involve two parts or less, skipping ' + def.typeId); + return null; } } + + const constraint = new schema.implementation(schema, fixedPart, movingPart, objects); + constraints.push(constraint); + if (movingPart) { + assignConstraint(movingPart, constraint); + } }); - return Array.from(bodies.values()); + const visited = new Set(); + const topoOrder: MShell[] = []; + for (let node of graph.keys()) { + if (visited.has(node)) { + continue; + } + dfs(node, (node, cb) => (graph.get(node)||[]).forEach(c => cb(c.fixedPart)), node => { + if (visited.has(node)) { + return; + } + visited.add(node); + topoOrder.push(node); + }); + } + + return topoOrder.reverse().map(model => ({ + model, + constraints: graph.get(model)||[] + })); } + +// function buildAssemblyQueue1(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]): RigidBody[] { +// +// const constraints: AssemblyConstraint[] = []; +// +// constraintDefs.forEach(def => { +// const schema = AssemblyConstraints[def.typeId]; +// if (!schema) { +// console.error('reference to nonexistent constraint ' + def.typeId); +// return null; +// } +// const constraint = new AssemblyConstraint(); +// constraint.schema = schema; +// for (const id of def.objects) { +// const modelObject = cadRegistry.find(id); +// if (!modelObject) { +// console.warn('skipping constraint referring to nonexistent object ' + id); +// return null; +// } +// constraint.objects.push(modelObject); +// } +// constraints.push(constraint); +// }); +// +// +// const bodies = new Map(); +// function body(obj: MObject) { +// let rigidBody = bodies.get(obj); +// if (!rigidBody) { +// rigidBody = new RigidBody(); +// rigidBody.model = obj; +// bodies.set(obj, rigidBody); +// } +// return rigidBody; +// } +// +// function link(a: MObject, b: MObject, constr: AssemblyConstraint) { +// const rigidBodyA = body(a); +// const rigidBodyB = body(b); +// +// let arr = rigidBodyA.relationships.get(rigidBodyB); +// if (!arr) { +// arr = []; +// rigidBodyA.relationships.set(rigidBodyB, arr); +// } +// arr.push(constr) +// } +// +// constraints.forEach(constr => { +// const roots = new Set(); +// constr.objects.forEach(o => roots.add(o.root)); +// const arr: MObject[] = Array.from(roots); +// for (let i = 0; i < arr.length; i++) { +// for (let j = i+1; j < arr.length; j++) { +// link(arr[i], arr[j], constr); +// link(arr[j], arr[i], constr); +// } +// } +// }); +// +// return Array.from(bodies.values()).reverse(); +// } + export function launchAssembly(assemblyProcess: AssemblyProcess): void { + assemblyProcess.begin(); + while (!assemblyProcess.isDone()) { assemblyProcess.step(); if (assemblyProcess.errorStep !== null) { @@ -156,125 +219,42 @@ export function launchAssembly(assemblyProcess: AssemblyProcess): void { } -function addToStage(stage: ISolveStage, object: AssemblyNode) { - stage.objects.add(object); - object.stage = stage; -} +function solve(constraints: AssemblyConstraint[], freeBody: MShell, location: Matrix3): SolveStatus { + for (let constr of constraints) { + freeBody.assemblyDOF = constr.apply(freeBody.assemblyDOF); -function solve(constraints: AssemblyConstraint[], freeBody: MObject, orientation: boolean): SolveStatus { - - - if (!(freeBody instanceof MShell)) { - throw 'unsupported: needs location implementation'; } - const readOnlyStage: ISolveStage = { - objects: new Set(), - index: 0 - }; - - const stage: ISolveStage = { - objects: new Set(), - index: 1 - }; - - - // const assemblyNodes: AssemblyNode = orientation ? : ; - - const solvingConstraints = []; - - constraints.forEach(c => { - const nodes = c.schema.defineAssemblyScope(c.objects); - - nodes.forEach(o => { - if (o.model.root === freeBody) { - addToStage(stage, o); - } else { - addToStage(readOnlyStage, o); - } - }); - - solvingConstraints.push(new AlgNumConstraint(orientation ? c.schema.orientation : c.schema.translation, nodes)); - }); - - addToStage(stage, freeBody.assemblyNodes.location); - - const system = new AlgNumSubSystem(() => 0.001, val => val, stage); - system.startTransaction(); - solvingConstraints.forEach(c => { - system.addConstraint(c); - }); - stage.objects.forEach(solveObject => { - const assemblyNode = solveObject as AssemblyNode; - const internalConstraints = assemblyNode.createConsistencyConstraints(); - internalConstraints.forEach(c => { - c.internal = true; - system.addConstraint(c); - }); - if (assemblyNode.model.root === freeBody) { - - const rigidBodyLinks = orientation ? - assemblyNode.createOrientationRelationship(freeBody.assemblyNodes.location): - assemblyNode.createTranslationRelationship(freeBody.assemblyNodes.location); - - rigidBodyLinks.forEach(c => { - c.internal = true; - system.addConstraint(c); - }); - } - }); - - system.finishTransaction(); - system.solveFine(); - - return system.solveStatus; -} - - -function applyLocation(shell: MShell): void { - - const targetLocation = shell.assemblyNodes.location; - - - - shell.location$.update(mx => targetLocation.toMatrix()); -} - -function applyResults(shell: MShell, targetCsysParams: AssemblyCSysNode): void { - const [ - ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz - ] = targetCsysParams.params.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); - // }); + return { + success: true, + error: 0 + } } +// function solveTranslation(constraints: AssemblyConstraint[], freeBody: MObject, location: Matrix3): SolveStatus { +// +// if (!(freeBody instanceof MShell)) { +// throw 'unsupported: needs location implementation'; +// } +// +// let trState: TranslationState = new TranslationState3DOF(); +// +// for (let constr of constraints) { +// +// const dir = constr.schema.translation(constr.objects, freeBody); +// +// trState = trState.applyConstraint(dir, location); +// +// } +// +// return { +// success: true, +// error: 0 +// } +// +// } + + diff --git a/web/app/cad/assembly/constraints/faceTouchAlign.ts b/web/app/cad/assembly/constraints/faceTouchAlign.ts new file mode 100644 index 00000000..7cff5daa --- /dev/null +++ b/web/app/cad/assembly/constraints/faceTouchAlign.ts @@ -0,0 +1,40 @@ +import {AssemblyConstraint, AssemblyConstraintSchema} from "../assemblyConstraint"; +import {MObject} from "../../model/mobject"; +import {NoIcon} from "../../../sketcher/icons/NoIcon"; +import {AssemblyDOF} from "../dof/assemblyDOF"; +import {MFace} from "../../model/mface"; +import { MShell } from "../../model/mshell"; + +export class FaceTouchAlignConstraint extends AssemblyConstraint { + + fixedFace: MFace; + movingFace: MFace; + + constructor(schema: AssemblyConstraintSchema, fixedPart: MShell, movingPart: MShell, objects: MObject[]) { + super(schema, fixedPart, movingPart, objects); + this.movingFace = objects[0] as MFace; + this.fixedFace = objects[1] as MFace; + } + + apply(dof: AssemblyDOF) { + return dof.applyTouchAlign(this); + } + +} + +export const FaceTouchAlign : AssemblyConstraintSchema = { + + id: 'FaceTouchAlign', + name: 'Face Touch Align', + icon: NoIcon, + + selectionMatcher: { + selector: 'matchAll', + types: ['face'], + minQuantity: 2 + }, + + implementation: FaceTouchAlignConstraint + + +}; \ No newline at end of file diff --git a/web/app/cad/assembly/constraints3d.ts b/web/app/cad/assembly/constraints3d.ts index cb456233..e69de29b 100644 --- a/web/app/cad/assembly/constraints3d.ts +++ b/web/app/cad/assembly/constraints3d.ts @@ -1,615 +0,0 @@ -import {COS_FN, Polynomial, POW_1_FN, POW_2_FN, SIN_FN} from "../../sketcher/constr/polynomial"; -import {NoIcon} from "../../sketcher/icons/NoIcon"; -import {ConstraintSchema} from "../../sketcher/constr/ANConstraints"; -import {MObject} from "../model/mobject"; -import {AssemblyNode} from "./assembly"; -import {IconType} from "react-icons"; -import Vector from "math/vector"; - - -export const Constraints3D = { - - PlaneOppositeNormals: { - id: 'PlaneOppositeNormals', - name: 'Plane Opposite Normals', - icon: NoIcon, - - defineParamsScope: ([plane1, plane2], cb) => { - cb(plane1.theta); - cb(plane1.phi); - cb(plane2.theta); - cb(plane2.phi); - }, - - collectPolynomials: (polynomials, params) => { - - const [ - theta1, phi1, theta2, phi2 - ] = params; - - // nx1, ny1, nz1, nx2, ny2, nz2 - - // sin(theta) * cos(phi), - // sin(theta) * sin(phi), - // cos(theta), - - - // const p = new Polynomial(1) - // .monomial() - // .term(theta1, SIN_FN) - // .term(phi1, COS_FN) - // .term(theta2, SIN_FN) - // .term(phi2, COS_FN) - // .monomial() - // .term(theta1, SIN_FN) - // .term(phi1, SIN_FN) - // - // .term(theta2, SIN_FN) - // .term(phi2, SIN_FN) - // .monomial() - // .term(theta1, COS_FN) - // .term(theta2, COS_FN); - - // 180 - theta1 - polynomials.push( - new Polynomial(Math.PI) - .monomial(-1) - .term(theta1, POW_1_FN) - .monomial(-1) - .term(theta2, POW_1_FN) - ); - polynomials.push( - new Polynomial(Math.PI) - .monomial(1) - .term(phi1, POW_1_FN) - .monomial(-1) - .term(phi2, POW_1_FN) - ); - } - }, - - PlaneEqualDepth: { - id: 'PlaneEqualDepth', - name: 'Plane Equal Depth', - icon: NoIcon, - - defineParamsScope: ([plane1, plane2], cb) => { - cb(plane1.w); - cb(plane2.w); - }, - - collectPolynomials: (polynomials, params) => { - - const [ - w1, w2 - ] = params; - - polynomials.push( - new Polynomial(0) - .monomial(1) - .term(w1, POW_1_FN) - .monomial() - .term(w2, POW_1_FN) - - ); - } - }, - - UnitVectorConsistency: { - id: 'UnitVectorConsistency', - name: 'UnitVectorConsistency', - icon: NoIcon, - - defineParamsScope: ([vec], cb) => { - //don't change to generic way it can a plane - cb(vec.x); - cb(vec.y); - cb(vec.z); - }, - - collectPolynomials: (polynomials, params) => { - - const [x, y, z] = params; - - polynomials.push( - new Polynomial(-1) - .monomial() - .term(x, POW_2_FN) - .monomial() - .term(y, POW_2_FN) - .monomial() - .term(z, POW_2_FN) - ); - } - }, - - CSysConsistency: { - id: 'CSysConsistency', - name: 'CSysConsistency', - icon: NoIcon, - - defineParamsScope: ([csys], cb) => { - 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); - }, - - collectPolynomials: (polynomials, params) => { - - const [ - ix, - iy, - iz, - jx, - jy, - jz, - kx, - ky, - kz] = params; - - //let's keep matrix orthogonal and unit basis - polynomials.push(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)); - - polynomials.push(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)); - - polynomials.push(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)); - - polynomials.push(new Polynomial(-1) - .monomial() - .term(ix, POW_2_FN) - .monomial() - .term(iy, POW_2_FN) - .monomial() - .term(iz, POW_2_FN)); - - polynomials.push(new Polynomial(-1) - .monomial() - .term(jx, POW_2_FN) - .monomial() - .term(jy, POW_2_FN) - .monomial() - .term(jz, POW_2_FN)); - - polynomials.push(new Polynomial(-1) - .monomial() - .term(kx, POW_2_FN) - .monomial() - .term(ky, POW_2_FN) - .monomial() - .term(kz, POW_2_FN)); - }, - }, - - PlaneNormalLink: { - id: 'PlaneNormalLink', - name: 'Plane Normal Link', - icon: NoIcon, - - defineParamsScope: ([location, plane], cb) => { - cb(location.alpha); - cb(location.beta); - cb(location.gamma); - - cb(plane.theta); - cb(plane.phi); - - - }, - - collectPolynomials: (polynomials, params, _, objects) => { - const [csys, plane] = objects; - - const {x: nStarX, y: nStarY, z: nStarZ} = plane.getNormal(); - - const [alpha, beta, gamma, theta, phi] = params; - - // return new Vector( - // Math.sin(theta) * Math.cos(phi), - // Math.sin(theta) * Math.sin(phi), - // Math.cos(theta), - // ) - - // 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; - - // cos(alpha)*cos(beta), cos(alpha)*sin(beta)*sin(gamma) - sin(alpha)*cos(gamma), cos(alpha)*sin(beta)*cos(gamma) + sin(alpha)*sin(gamma), - // sin(alpha)*cos(beta), sin(alpha)*sin(beta)*sin(gamma) + cos(alpha)*cos(gamma), sin(alpha)*sin(beta)*cos(gamma) - cos(alpha)*sin(gamma), - // -sin(beta), cos(beta)*sin(gamma), cos(beta)*cos(gamma) - - polynomials.push( - new Polynomial(0) - .monomial(-1) - .term(theta, SIN_FN) - .term(phi, COS_FN) - .monomial(nStarX) - .term(alpha, COS_FN) - .term(beta, COS_FN) - - .monomial(nStarY) - .term(alpha, COS_FN) - .term(beta, SIN_FN) - .term(gamma, SIN_FN) - - .monomial(-nStarY) - .term(alpha, SIN_FN) - .term(gamma, COS_FN) - - .monomial(nStarZ) - .term(alpha, COS_FN) - .term(beta, SIN_FN) - .term(gamma, COS_FN) - - .monomial(nStarZ) - .term(alpha, SIN_FN) - .term(gamma, SIN_FN) - - ); - - // sin(alpha)*cos(beta), sin(alpha)*sin(beta)*sin(gamma) + cos(alpha)*cos(gamma), sin(alpha)*sin(beta)*cos(gamma) - cos(alpha)*sin(gamma), - // Math.sin(theta) * Math.sin(phi), - - polynomials.push( - new Polynomial(0) - .monomial(-1) - .term(theta, SIN_FN) - .term(phi, SIN_FN) - - .monomial(nStarX) - .term(alpha, SIN_FN) - .term(beta, COS_FN) - - .monomial(nStarY) - .term(alpha, SIN_FN) - .term(beta, SIN_FN) - .term(gamma, SIN_FN) - - .monomial(nStarY) - .term(alpha, COS_FN) - .term(gamma, COS_FN) - - .monomial(nStarZ) - .term(alpha, SIN_FN) - .term(beta, SIN_FN) - .term(gamma, COS_FN) - - .monomial(-nStarZ) - .term(alpha, COS_FN) - .term(gamma, SIN_FN) - - ); - - // -sin(beta), cos(beta)*sin(gamma), cos(beta)*cos(gamma) - - polynomials.push( - new Polynomial(0) - .monomial(-1) - .term(theta, COS_FN) - - .monomial(-nStarX) - .term(beta, SIN_FN) - - .monomial(nStarY) - .term(beta, COS_FN) - .term(gamma, SIN_FN) - - .monomial(nStarZ) - .term(beta, COS_FN) - .term(gamma, COS_FN) - ); - - } - - }, - - PlaneDepthLink: { - id: 'PlaneDepthLink', - name: 'PlaneDepthLink', - icon: NoIcon, - - defineParamsScope: ([location, plane], cb) => { - cb(location.dx); - cb(location.dy); - cb(location.dz); - cb(plane.w); - }, - - collectPolynomials: (polynomials, params, _, objects) => { - const [location, plane] = objects; - const [ox, oy, oz, w] = params; - const {x: xP, y: yP, z: zP} = plane.toNormalVector(); - - // __DEBUG__.AddNormal() - - const {x: nStarX, y: nStarY, z: nStarZ} = plane.getNormal(); - const nStarW = plane.getDepth(); - - const pStar = plane.getNormal().multiply(nStarW); - const p0 = location.rotationMatrix().apply(pStar); - const w0 = p0.length(); - - // 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(-xP * w0) - .monomial(xP) - .term(w, POW_1_FN) - .monomial(-1) - .term(ox, POW_1_FN) - ); - - polynomials.push( - new Polynomial(-yP * w0) - .monomial(yP) - .term(w, POW_1_FN) - .monomial(-1) - .term(oy, POW_1_FN) - - ); - - polynomials.push( - new Polynomial(-zP * w0) - .monomial(zP) - .term(w, POW_1_FN) - .monomial(-1) - .term(oz, POW_1_FN) - ); - - } - }, - - RigidBodyLink3x3: { - id: 'RigidBodyLink3x3', - name: 'RigidBodyLink3x3', - icon: NoIcon, - - defineParamsScope: ([csys, vec], cb) => { - 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); - cb(vec.x); - cb(vec.y); - cb(vec.z); - }, - - collectPolynomials: (polynomials, params, _, objects) => { - const [csys, vec] = objects; - - const {x: nStarX, y: nStarY, z: nStarZ} = vec.getVector(); - - const [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(nStarX) - .term(ix, POW_1_FN) - .monomial(nStarY) - .term(jx, POW_1_FN) - .monomial(nStarZ) - .term(kx, POW_1_FN) - ); - - polynomials.push( - 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), - - ); - - polynomials.push( - 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) - ); - - } - }, - - 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 const AssemblyConstraints: { - [key: string]: AssemblyConstraintSchema -} = { - - 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, - ]; - }, - - orientation: ([plane1, plane2]) => { - - return new OrientationConstraint(plane1.); - - }, - - translation: Constraints3D.PlaneEqualDepth, - - } - -}; - -export class OrientationConstraint { - - vecA: Vector; - vecB: Vector; - - constructor(vecA: Vector, vecB: Vector) { - this.vecA = vecA; - this.vecB = vecB; - } - -} - -export interface AssemblyConstraintSchema { - - id: string, - name: string, - icon?: IconType, - - selectionMatcher?: { - selector: string, - types: any[], - minQuantity: number - }; - - defineAssemblyScope: (objects: MObject[]) => AssemblyNode[]; - - orientation: (objects: AssemblyNode[]) => OrientationConstraint, - translation: ConstraintSchema, - -} - diff --git a/web/app/cad/assembly/dof/PPDOF.ts b/web/app/cad/assembly/dof/PPDOF.ts new file mode 100644 index 00000000..8986312c --- /dev/null +++ b/web/app/cad/assembly/dof/PPDOF.ts @@ -0,0 +1,93 @@ +import {Matrix3, ORIGIN} from "math/l3space"; +import Vector from "math/vector"; +import {eqTol} from "../../../brep/geom/tolerance"; +import {FaceTouchAlignConstraint} from "../constraints/faceTouchAlign"; +import {Plane} from './../../../brep/geom/impl/plane'; +import {AssemblyDOF, ModificationResponse} from "./assemblyDOF"; +import {areEqual, clamp, DEG_RAD} from "../../../math/math"; +import {ConflictDOF} from "./conflictDOF"; + +const ANGULAR_ALLOWANCE = 10 * DEG_RAD; + +export class PPDOF implements AssemblyDOF { + + description = 'plane to plane'; + + plane: Plane; + + constructor(plane: Plane) { + this.plane = plane; + } + + + applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF { + + const rotationAxis = this.plane.normal; + + const vecA = constr.movingPart.location.applyNoTranslation(constr.movingFace.normal()).normalize(); + const vecB = constr.fixedPart.location.applyNoTranslation(constr.fixedFace.normal())._negate().normalize(); + + const cosA = clamp(rotationAxis.dot(vecA), -1, 1); + const cosB = clamp(rotationAxis.dot(vecB), -1, 1); + const sinA = clamp(rotationAxis.cross(vecA).length(), -1, 1); + const sinB = clamp(rotationAxis.cross(vecB).length(), -1, 1); + + const angA = Math.atan2(sinA, cosA); + const angB = Math.atan2(sinB, cosB); + + // it's not a tolerance + if (!areEqual(angA, angB, ANGULAR_ALLOWANCE)) { + console.log('constraint conflict'); + return new ConflictDOF(constr, 'unable to align faces with not matching angles with respect to plane to plane align degree of freedom'); + } + + const location = constr.movingPart.root.location; + + const rot = new Matrix3(); + + Matrix3.rotationFromVectorToVector(vecA, vecB, ORIGIN, rot); + + rot.combine3x3(location, location); + + const ptMoving = constr.movingPart.location.apply(constr.movingFace.csys.origin); + const ptFixed = constr.fixedPart.location.apply(constr.fixedFace.csys.origin); + + + const wA = vecB.dot(ptMoving); + const wB = vecB.dot(ptFixed); + + const dir = vecB.multiply(wB - wA); + + location.translateVec(dir); + + return this; + } + + rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + + translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse { + + const normal = this.plane.normal; + const illegalTranslation = !eqTol(normal.dot(dir), 0); + if (illegalTranslation && strict) { + return ModificationResponse.REJECTED; + } + + //fix it anyway to mitigate any rounding errors + + debugger; + + const y = normal.cross(dir)._normalize(); + const x = y.cross(normal)._normalize(); + + const u = x.dot(dir); + const fixedDir = x._multiply(u); + + location.translateVec(fixedDir); + + return illegalTranslation ? ModificationResponse.FIXED : ModificationResponse.SUCCESS; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/PPPPDOF.ts b/web/app/cad/assembly/dof/PPPPDOF.ts new file mode 100644 index 00000000..450ce2fe --- /dev/null +++ b/web/app/cad/assembly/dof/PPPPDOF.ts @@ -0,0 +1,31 @@ +import { Matrix3 } from "math/l3space"; +import Vector from "math/vector"; +import { FaceTouchAlignConstraint } from "../constraints/faceTouchAlign"; +import { Plane } from './../../../brep/geom/impl/plane'; +import { AssemblyDOF, ModificationResponse } from "./assemblyDOF"; +import { ConflictDOF } from './conflictDOF'; + +export class PPPPDOF implements AssemblyDOF { + + plane1: Plane; + plane2: Plane; + description = 'plane to plane twice'; + + constructor(plane1: Plane, plane2: Plane) { + this.plane1 = plane1; + this.plane2 = plane2; + } + + applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF { + return new ConflictDOF(constr, 'plane touch/align constraint cannot be applied when object is at ' + this.description + ' relationship'); + } + + rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + + translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/assemblyDOF.ts b/web/app/cad/assembly/dof/assemblyDOF.ts new file mode 100644 index 00000000..9e9ba0f5 --- /dev/null +++ b/web/app/cad/assembly/dof/assemblyDOF.ts @@ -0,0 +1,23 @@ +import Vector from "math/vector"; +import {Matrix3} from "math/l3space"; +import {FaceTouchAlignConstraint} from "../constraints/faceTouchAlign"; + + +export enum ModificationResponse { + + SUCCESS, FIXED, REJECTED + +} + +export interface AssemblyDOF { + + description: string; + + applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF; + + translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse; + + rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse; + + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/conflictDOF.ts b/web/app/cad/assembly/dof/conflictDOF.ts new file mode 100644 index 00000000..7c75a1b9 --- /dev/null +++ b/web/app/cad/assembly/dof/conflictDOF.ts @@ -0,0 +1,33 @@ + +import { Matrix3 } from "math/l3space"; +import Vector from "math/vector"; +import { AssemblyConstraint } from '../assemblyConstraint'; +import { FaceTouchAlignConstraint } from "../constraints/faceTouchAlign"; +import { AssemblyDOF, ModificationResponse } from "./assemblyDOF"; + +export class ConflictDOF implements AssemblyDOF { + + description = 'conflicting'; + + conflictingConstraint: AssemblyConstraint; + infoMessage: string; + + constructor(conflictingConstraint: AssemblyConstraint, infoMessage: string) { + this.conflictingConstraint = conflictingConstraint; + this.infoMessage = infoMessage; + } + + + applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF { + return this; + } + + rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + + translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/sixDOF.ts b/web/app/cad/assembly/dof/sixDOF.ts new file mode 100644 index 00000000..fa572e2f --- /dev/null +++ b/web/app/cad/assembly/dof/sixDOF.ts @@ -0,0 +1,39 @@ +import { Plane } from './../../../brep/geom/impl/plane'; +import {AssemblyDOF, ModificationResponse} from "./assemblyDOF"; +import Vector from "math/vector"; +import {Matrix3, ORIGIN} from "math/l3space"; +import {FaceTouchAlignConstraint} from "../constraints/faceTouchAlign"; +import {PPDOF} from "./PPDOF"; + +export class SixDOF implements AssemblyDOF { + + description = 'full freedom'; + + applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF { + + const vecA = constr.movingPart.location.applyNoTranslation(constr.movingFace.normal()); + const vecB = constr.fixedPart.location.applyNoTranslation(constr.fixedFace.normal())._negate(); + + const location = constr.movingPart.root.location; + + Matrix3.rotationFromVectorToVector(vecA, vecB, ORIGIN, location); + + const ptFixed = constr.fixedPart.location.apply(constr.fixedFace.favorablePoint); + const ptMoving = constr.movingPart.location.apply(constr.movingFace.favorablePoint); + + const dir = ptFixed._minus(ptMoving); + + location.translate(dir.x, dir.y, dir.z); + + return new PPDOF(new Plane(vecB.copy(), vecB.dot(ptFixed))); + } + + rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + + translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse { + return ModificationResponse.REJECTED; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyCSysNode.ts b/web/app/cad/assembly/nodes/assemblyCSysNode.ts deleted file mode 100644 index 68889c10..00000000 --- a/web/app/cad/assembly/nodes/assemblyCSysNode.ts +++ /dev/null @@ -1,85 +0,0 @@ -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"; -import Vector from "math/vector"; - -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); - - } - - - rotationMatrix() { - const { - ix, iy, iz, jx, jy, jz, kx, ky, kz - } = this; - - return new Matrix3().setBasis([ - new Vector(ix.get(), iy.get(), iz.get()), - new Vector(jx.get(), jy.get(), jz.get()), - new Vector(kx.get(), ky.get(), kz.get()), - ]); - - } - - createConsistencyConstraints() { - return [ - new AlgNumConstraint(Constraints3D.CSysConsistency, [this]) - ]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyLocationNode.ts b/web/app/cad/assembly/nodes/assemblyLocationNode.ts deleted file mode 100644 index d6710b0a..00000000 --- a/web/app/cad/assembly/nodes/assemblyLocationNode.ts +++ /dev/null @@ -1,81 +0,0 @@ -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"; -import Vector from "math/vector"; - -export class AssemblyLocationNode extends AssemblyNode { - - alpha = new Param(0, 'A'); - beta = new Param(0, 'B'); - gamma = new Param(0, 'G'); - dx = new Param(0, 'X'); - dy = new Param(0, 'Y'); - dz = new Param(0, 'Z'); - - getTransformation: () => Matrix3; - - constructor(model: MObject, getTransformation: () => Matrix3) { - super(model); - this.getTransformation = getTransformation; - } - - visitParams(cb) { - cb(this.alpha); - cb(this.beta); - cb(this.gamma); - } - - reset() { - const mx = this.getTransformation(); - this.alpha.set(0); - this.beta.set(0); - this.gamma.set(0); - } - - - rotationMatrix() { - - return new Matrix3().set3( - - ...this.rotationComponents() - - ); - - } - - rotationComponents(): [number, number, number, number, number, number, number, number, number] { - - const alpha = this.alpha.get(); - const beta = this.beta.get(); - const gamma = this.gamma.get(); - - const cos = Math.cos; - const sin = Math.sin; - - return [ - cos(alpha)*cos(beta), cos(alpha)*sin(beta)*sin(gamma) - sin(alpha)*cos(gamma), cos(alpha)*sin(beta)*cos(gamma) + sin(alpha)*sin(gamma), - sin(alpha)*cos(beta), sin(alpha)*sin(beta)*sin(gamma) + cos(alpha)*cos(gamma), sin(alpha)*sin(beta)*cos(gamma) - cos(alpha)*sin(gamma), - -sin(beta), cos(beta)*sin(gamma), cos(beta)*cos(gamma) - ] - } - - translationComponents(): [number, number, number] { - return [this.dx.get(), this.dy.get(), this.dz.get()]; - } - - toMatrix() { - const mx = this.rotationMatrix(); - mx.setTranslation(...this.translationComponents()); - return mx; - } - - createConsistencyConstraints() { - return [ - // new AlgNumConstraint(Constraints3D.CSysConsistency, [this]) - ]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyOrientationNode.ts b/web/app/cad/assembly/nodes/assemblyOrientationNode.ts deleted file mode 100644 index 4874254a..00000000 --- a/web/app/cad/assembly/nodes/assemblyOrientationNode.ts +++ /dev/null @@ -1,61 +0,0 @@ -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 AssemblyOrientationNode extends AssemblyNode { - - 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.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.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]) - ]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyPlaneNode.ts b/web/app/cad/assembly/nodes/assemblyPlaneNode.ts deleted file mode 100644 index ab069a27..00000000 --- a/web/app/cad/assembly/nodes/assemblyPlaneNode.ts +++ /dev/null @@ -1,68 +0,0 @@ -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"; -import {clamp} from "../../../math/math"; -import {AssemblyLocationNode} from "./assemblyLocationNode"; - -export class AssemblyPlaneNode extends AssemblyNode { - - theta = new Param(0, 'T'); - phi = new Param(0, 'P'); - 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.theta); - cb(this.phi); - cb(this.w); - } - - reset() { - const {x, y, z} = this.getNormal(); - const w = this.getDepth(); - const phi = Math.atan2(y, x); - const theta = Math.acos(clamp(z, -1, 1)); - - this.theta.set(theta); - this.phi.set(phi); - - this.w.set(w); - } - - toNormalVector() { - const theta = this.theta.get(); - const phi = this.phi.get(); - return new Vector( - Math.sin(theta) * Math.cos(phi), - Math.sin(theta) * Math.sin(phi), - Math.cos(theta), - ) - } - - createConsistencyConstraints() { - return [ - - ]; - } - - - createOrientationRelationship(location: AssemblyLocationNode): AlgNumConstraint[] { - return [new AlgNumConstraint(Constraints3D.PlaneNormalLink, [location, this])]; - } - - createTranslationRelationship(location: AssemblyLocationNode): AlgNumConstraint[] { - return [new AlgNumConstraint(Constraints3D.PlaneDepthLink, [location, this])]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyScalarNode.ts b/web/app/cad/assembly/nodes/assemblyScalarNode.ts deleted file mode 100644 index f8ee7f18..00000000 --- a/web/app/cad/assembly/nodes/assemblyScalarNode.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {AssemblyNode} from "../assembly"; -import {Param} from "../../../sketcher/shapes/param"; -import {MObject} from "../../model/mobject"; - -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); - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyTranslationNode.ts b/web/app/cad/assembly/nodes/assemblyTranslationNode.ts deleted file mode 100644 index 3136e51b..00000000 --- a/web/app/cad/assembly/nodes/assemblyTranslationNode.ts +++ /dev/null @@ -1,40 +0,0 @@ -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 AssemblyTranslationNode 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 [ - - ]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyUnitVectorNode.ts b/web/app/cad/assembly/nodes/assemblyUnitVectorNode.ts deleted file mode 100644 index bfffd4e9..00000000 --- a/web/app/cad/assembly/nodes/assemblyUnitVectorNode.ts +++ /dev/null @@ -1,45 +0,0 @@ -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 {AssemblyOrientationNode} from "./assemblyOrientationNode"; - -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]) - ]; - } - - createRigidBodyOrientationRelationship(orientationNode: AssemblyOrientationNode): AlgNumConstraint[] { - return [new AlgNumConstraint(Constraints3D.RigidBodyLink3x3, [orientationNode, this])]; - } - - -} \ No newline at end of file diff --git a/web/app/cad/assembly/nodes/assemblyVectorNode.ts b/web/app/cad/assembly/nodes/assemblyVectorNode.ts deleted file mode 100644 index ab5bbe01..00000000 --- a/web/app/cad/assembly/nodes/assemblyVectorNode.ts +++ /dev/null @@ -1,40 +0,0 @@ -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]) - ]; - } - -} \ No newline at end of file diff --git a/web/app/cad/assembly/ui/AssemblyView.tsx b/web/app/cad/assembly/ui/AssemblyView.tsx index 16b54e5c..05a05995 100644 --- a/web/app/cad/assembly/ui/AssemblyView.tsx +++ b/web/app/cad/assembly/ui/AssemblyView.tsx @@ -1,17 +1,15 @@ -import React, {useContext, useEffect, useState} from 'react'; +import React, {useContext, useEffect} from 'react'; import {useStream} from "ui/effects"; import {Status} from "ui/components/Status"; -import Folder from "ui/components/Folder"; -import {AssemblyConstraints, 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"; -import {AssemblyProcess} from "../assemblySolver"; import {StepByStepSimulation} from "./StepByStepSimulation"; +import {AssemblyConstraintDefinition} from "../assemblyConstraint"; +import {AssemblyConstraintsSchemas} from "../assemblySchemas"; export function AssemblyView() { @@ -65,8 +63,8 @@ export function AssemblyConstraintButton({prefix='', constraint: c, ...props}: { useEffect(() => withdraw, [c]); - const schema = AssemblyConstraints[c.typeId]; - if (schema === null) { + const schema = AssemblyConstraintsSchemas[c.typeId]; + if (!schema) { return
Invalid Constraint {c.typeId}
} diff --git a/web/app/cad/assembly/ui/ModellerContextualActions.tsx b/web/app/cad/assembly/ui/ModellerContextualActions.tsx index c4fd4927..fc5a8967 100644 --- a/web/app/cad/assembly/ui/ModellerContextualActions.tsx +++ b/web/app/cad/assembly/ui/ModellerContextualActions.tsx @@ -2,8 +2,9 @@ import React, {useContext} from 'react'; import {AppContext} from "../../dom/components/AppContext"; import {useStream} from "ui/effects"; import {Dialog} from "ui/components/Dialog"; -import {AssemblyConstraints, AssemblyConstraintSchema, Constraints3D} from "../constraints3d"; import {matchAvailableSubjects, MatchIndex, matchSelection} from "../../../sketcher/selectionMatcher"; +import {AssemblyConstraintSchema} from "../assemblyConstraint"; +import {AssemblyConstraintsSchemas} from "../assemblySchemas"; export function ModellerContextualActions() { @@ -17,7 +18,7 @@ export function ModellerContextualActions() { const entities = selection.map(ctx.cadRegistry.find); - const allConstraints = Object.values(AssemblyConstraints) as AssemblyConstraintSchema[]; + const allConstraints = Object.values(AssemblyConstraintsSchemas) as AssemblyConstraintSchema[]; const availableConstraints = matchAvailableSubjects(entities, allConstraints) as AssemblyConstraintSchema[]; if (availableConstraints.length === 0) { diff --git a/web/app/cad/assembly/ui/StepByStepSimulation.tsx b/web/app/cad/assembly/ui/StepByStepSimulation.tsx index baaca170..b86cc6a0 100644 --- a/web/app/cad/assembly/ui/StepByStepSimulation.tsx +++ b/web/app/cad/assembly/ui/StepByStepSimulation.tsx @@ -17,12 +17,7 @@ export function StepByStepSimulation() { function stepByStepSimulation() { if (process === null || process.isDone()) { const newProcess = new AssemblyProcess(ctx.cadRegistry, constraints); - newProcess.queue.forEach(rb => { - if (rb.model.root instanceof MShell) { - rb.model.root.location$.next(new Matrix3()); - } - }); - newProcess.step(); + newProcess.begin(); setProcess(newProcess); } else { process.step(); diff --git a/web/app/cad/craft/cadRegistryPlugin.ts b/web/app/cad/craft/cadRegistryPlugin.ts index 6fcb5276..ca4e39cb 100644 --- a/web/app/cad/craft/cadRegistryPlugin.ts +++ b/web/app/cad/craft/cadRegistryPlugin.ts @@ -1,12 +1,13 @@ import {MShell} from '../model/mshell'; import {MObject} from "../model/mobject"; import {ApplicationContext} from "context"; +import {Stream} from "lstream"; export function activate(ctx: ApplicationContext) { const {streams, services} = ctx; - const shells$ = streams.craft.models.map(models => models.filter(m => m instanceof MShell)).remember(); + const shells$: Stream = streams.craft.models.map(models => models.filter(m => m instanceof MShell)).remember(); const modelIndex$ = streams.craft.models.map(models => { const index = new Map(); models.forEach(model => model.traverse(m => index.set(m.id, m))); @@ -21,7 +22,7 @@ export function activate(ctx: ApplicationContext) { const index = () => modelIndex$.value; - function getAllShells() { + function getAllShells(): MShell[] { return streams.cadRegistry.shells.value; } @@ -79,7 +80,7 @@ export function activate(ctx: ApplicationContext) { export interface CadRegistry { - getAllShells(): MObject[]; + getAllShells(): MShell[]; findShell(id: string): MObject; findFace(id: string): MObject; findEdge(id: string): MObject; diff --git a/web/app/cad/location/LocationDialog.tsx b/web/app/cad/location/LocationDialog.tsx index 5cdcd023..fc03fb83 100644 --- a/web/app/cad/location/LocationDialog.tsx +++ b/web/app/cad/location/LocationDialog.tsx @@ -20,17 +20,17 @@ export function LocationDialog() { const [location, setLocation] = useStreamWithUpdater(() => req ? req.shell.location$ : never()); const setX = useCallback(x => { - location.origin.x = parseFloat(x); + location.tx = parseFloat(x); setLocation(location); }, [setLocation]); const setY = useCallback(y => { - location.origin.y = parseFloat(y); + location.ty = parseFloat(y); setLocation(location); }, [setLocation]); const setZ = useCallback(z => { - location.origin.z = parseFloat(z); + location.tz = parseFloat(z); setLocation(location); }, [setLocation]); // @@ -81,15 +81,15 @@ export function LocationDialog() { - + - + - + diff --git a/web/app/cad/model/mdatum.ts b/web/app/cad/model/mdatum.ts index 72245511..f585c92b 100644 --- a/web/app/cad/model/mdatum.ts +++ b/web/app/cad/model/mdatum.ts @@ -1,6 +1,7 @@ -import {MObject, MObjectIdGenerator} from './mobject'; +import {MObject, MObjectIdGenerator, MRootObject} from './mobject'; import CSys from "math/csys"; import Vector from "math/vector"; +import {Matrix3} from "math/l3space"; export class MDatum extends MObject { diff --git a/web/app/cad/model/mface.ts b/web/app/cad/model/mface.ts index 794fa319..56ea06e8 100644 --- a/web/app/cad/model/mface.ts +++ b/web/app/cad/model/mface.ts @@ -1,16 +1,13 @@ import {MObject} from './mobject'; import Vector from 'math/vector'; -import {BasisForPlane} from 'math/l3space'; +import {Basis, BasisForPlane} from 'math/l3space'; import {MSketchObject} from './msketchObject'; import {EMPTY_ARRAY} from 'gems/iterables'; import CSys from 'math/csys'; import {MSketchLoop} from './mloop'; import {ProductionInfo} from './productionInfo'; import {MBrepShell, MShell} from "./mshell"; -import {AssemblyUnitVectorNode} from "../assembly/nodes/assemblyUnitVectorNode"; -import {AssemblyScalarNode} from "../assembly/nodes/assemblyScalarNode"; -import {AssemblyVectorNode} from "../assembly/nodes/assemblyVectorNode"; -import {AssemblyPlaneNode} from "../assembly/nodes/assemblyPlaneNode"; +import BBox from "../../math/bbox"; export class MFace extends MObject { @@ -22,12 +19,6 @@ export class MFace extends MObject { sketch: any; brepFace: any; - assemblyNodes: { - normal: AssemblyUnitVectorNode - plane: AssemblyPlaneNode, - // w: AssemblyScalarNode - }; - private _csys: any; private w: number; private _basis: [Vector, Vector, Vector]; @@ -42,11 +33,6 @@ export class MFace extends MObject { this.sketchObjects = []; this.sketchLoops = []; this._csys = csys; - this.assemblyNodes = { - normal: new AssemblyUnitVectorNode(this, () => this.normal()), - // w: new AssemblyScalarNode(this, 'W', () => this.depth()) - plane: new AssemblyPlaneNode(this, () => this.normal(), () => this.depth()) - }; } normal(): Vector { @@ -58,19 +44,19 @@ export class MFace extends MObject { return this.w; } - basis() { + basis(): Basis { if (!this._basis) { this._basis = [this.csys.x, this.csys.y, this.csys.z]; } return this._basis; } - get csys() { + get csys(): CSys { this.evalCSys(); return this._csys; } - get isPlaneBased() { + get isPlaneBased(): boolean { return this.surface.simpleSurface && this.surface.simpleSurface.isPlane; } @@ -185,10 +171,15 @@ export class MFace extends MObject { return this.shell; } + get favorablePoint() { + return this.csys.origin; + } } export class MBrepFace extends MFace { + #favorablePoint: Vector; + constructor(id, shell, brepFace) { super(id, shell, brepFace.surface); this.id = id; @@ -213,4 +204,21 @@ export class MBrepFace extends MFace { } return bounds; } + + get favorablePoint() { + if (!this.#favorablePoint) { + const bbox = new BBox(); + const outerPoly = this.brepFace.outerLoop.asPolygon(); + if (outerPoly) { + outerPoly.forEach(pt => { + const pt2d = this.csys.outTransformation.apply(pt); + bbox.checkPoint(pt2d); + }); + this.#favorablePoint = this.csys.inTransformation.apply(bbox.center()); + } else { + this.#favorablePoint = this.surface.pointInMiddle; + } + } + return this.#favorablePoint; + } } diff --git a/web/app/cad/model/mobject.ts b/web/app/cad/model/mobject.ts index 3b7921c2..e81f9d7c 100644 --- a/web/app/cad/model/mobject.ts +++ b/web/app/cad/model/mobject.ts @@ -1,4 +1,5 @@ import {AssemblyNode} from "../assembly/assembly"; +import {IDENTITY_MATRIX, Matrix3} from "math/l3space"; export abstract class MObject { @@ -29,6 +30,10 @@ export abstract class MObject { } return obj; } + + get location() { + return IDENTITY_MATRIX; + } } export const MObjectIdGenerator = { diff --git a/web/app/cad/model/mshell.ts b/web/app/cad/model/mshell.ts index 744eaf0c..51b13957 100644 --- a/web/app/cad/model/mshell.ts +++ b/web/app/cad/model/mshell.ts @@ -5,11 +5,6 @@ import {MVertex} from './mvertex'; import CSys from 'math/csys'; import {Matrix3} from "math/l3space"; import {state, StateStream} from "lstream"; -import {AssemblyCSysNode} from "../assembly/nodes/assemblyCSysNode"; -import {AssemblyOrientationNode} from "../assembly/nodes/assemblyOrientationNode"; -import {AssemblyVectorNode} from "../assembly/nodes/assemblyVectorNode"; -import {AssemblyTranslationNode} from "../assembly/nodes/assemblyTranslationNode"; -import {AssemblyLocationNode} from "../assembly/nodes/assemblyLocationNode"; export class MShell extends MObject { @@ -18,26 +13,14 @@ export class MShell extends MObject { csys: CSys; shell; - faces = []; + faces = []; edges = []; vertices = []; location$: StateStream = state(new Matrix3()); - assemblyNodes: { - location: AssemblyLocationNode, - orientation: AssemblyOrientationNode, - translation: AssemblyTranslationNode, - }; - constructor() { super(MShell.TYPE, MObjectIdGenerator.next(MShell.TYPE, 'S')); - // @ts-ignore - this.assemblyNodes = { - location: new AssemblyLocationNode(this, () => new Matrix3() ), - orientation: new AssemblyOrientationNode( this, () => new Matrix3() ) - }; - } traverse(callback: (obj: MObject) => void): void { @@ -50,6 +33,10 @@ export class MShell extends MObject { get parent() { return null; } + + get location() { + return this.location$.value; + } } export class MBrepShell extends MShell { diff --git a/web/app/sketcher/selectionMatcher.js b/web/app/sketcher/selectionMatcher.ts similarity index 96% rename from web/app/sketcher/selectionMatcher.js rename to web/app/sketcher/selectionMatcher.ts index bd9f84d1..2e1e526b 100644 --- a/web/app/sketcher/selectionMatcher.js +++ b/web/app/sketcher/selectionMatcher.ts @@ -1,4 +1,14 @@ +export interface SelectionMatcher { + + selector: string, + + types: any[], + + minQuantity: number + +} + export function matchAvailableSubjects(selection, subjects) { let matched = []; @@ -95,6 +105,8 @@ export class MatchIndex { typeMap = new Map(); overallHits = 0; + result: any[]; + selection: any; constructor(selection) { this.selection = selection; diff --git a/web/app/sketcher/shapes/sketch-object.ts b/web/app/sketcher/shapes/sketch-object.ts index 0d774ce1..e9a3c2a8 100644 --- a/web/app/sketcher/shapes/sketch-object.ts +++ b/web/app/sketcher/shapes/sketch-object.ts @@ -4,7 +4,7 @@ import {Styles} from "../styles"; import {NoIcon} from "../icons/NoIcon"; import {Layer, Viewer} from "../viewer2d"; import {NOOP} from "gems/func"; -import {SolvableObject} from "../constr/solveObject"; +import {SolvableObject} from "../constr/solvableObject"; export abstract class SketchObject extends Shape implements SolvableObject {