basic DOFs for assemblies

This commit is contained in:
Val Erastov (xibyte) 2020-07-02 23:45:53 -07:00
parent c9ac719f83
commit 801480c5f4
37 changed files with 721 additions and 1410 deletions

View file

@ -1,7 +1,9 @@
export function dfs<T>(node:T,
children: (node: T, consumer: (node: T) => void) => void,
callback: (node) => any): boolean {
const visited = new Set<T>();
export function dfs(node, children, callback) {
const visited = new Set();
const stack = []; const stack = [];
stack.push(node); stack.push(node);
while (stack.length) { while (stack.length) {
@ -17,8 +19,11 @@ export function dfs(node, children, callback) {
} }
} }
export function bfs(node, children, callback) { export function bfs<T>(node:T,
const visited = new Set(); children: (node: T, consumer: (node: T) => void) => void,
callback: (node) => any): boolean {
const visited = new Set<T>();
const queue = []; const queue = [];
queue.unshift(node); queue.unshift(node);
while (queue.length) { while (queue.length) {

View file

@ -109,6 +109,13 @@ export class Matrix3 {
return this; return this;
}; };
translateVec({x, y, z}: Vector): Matrix3 {
this.tx += x;
this.ty += y;
this.tz += z;
return this;
};
set3( set3(
mxx: number, mxy: number, mxz: number, mxx: number, mxy: number, mxz: number,
myx: number, myy: number, myz: number, myx: number, myy: number, myz: number,
@ -263,6 +270,53 @@ export class Matrix3 {
return m; 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 { _apply(vector: Vector): Vector {
return this.__apply(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 { static rotateMatrix(angle: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 {
var sin = Math.sin(angle); const sin = Math.sin(angle);
var cos = Math.cos(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 axisX, axisY, axisZ;
var m = matrix || new Matrix3(); var m = matrix || new Matrix3();
@ -360,10 +429,11 @@ export class Matrix3 {
this.tz = tz; this.tz = tz;
return this; 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; let alignPlane, x, y;
if (Math.abs(normal.dot(alignY)) < 0.5) { if (Math.abs(normal.dot(alignY)) < 0.5) {
alignPlane = normal.cross(alignY); alignPlane = normal.cross(alignY);
@ -375,4 +445,6 @@ function BasisForPlane(normal: Vector, alignY: Vector = AXIS.Y, alignZ: Vector =
return [x, y, normal]; return [x, y, normal];
} }
export const IDENTITY_MATRIX = Object.freeze(new Matrix3());
export {ORIGIN, IDENTITY_BASIS, AXIS, BasisForPlane}; export {ORIGIN, IDENTITY_BASIS, AXIS, BasisForPlane};

View file

@ -6,9 +6,10 @@
"esModuleInterop": true, "esModuleInterop": true,
"allowJs": true, "allowJs": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"target": "ES5", "target": "ES2015",
"baseUrl": ".", "baseUrl": ".",
"downlevelIteration": true, "downlevelIteration": true,
"module": "commonJS",
"paths": { "paths": {
"*": [ "*": [
"modules/*", "modules/*",

View file

@ -1,11 +1,19 @@
import Vector from "math/vector";
export class Line { export class Line {
p0: Vector;
v: Vector;
private _pointsCache: Map<any, Vector>;
isLine: boolean;
static fromTwoPlanesIntersection: (plane1, plane2) => Line;
static fromSegment: (a, b) => Line;
constructor(p0, v) { constructor(p0, v) {
throw 'only nurbs for now'
this.p0 = p0; this.p0 = p0;
this.v = v; this.v = v;
this._pointsCache = new Map();
} }
intersectSurface(surface) { intersectSurface(surface) {
@ -13,7 +21,8 @@ export class Line {
const s0 = surface.normal.multiply(surface.w); const s0 = surface.normal.multiply(surface.w);
return surface.normal.dot(s0.minus(this.p0)) / surface.normal.dot(this.v); // 4.7.4 return surface.normal.dot(s0.minus(this.p0)) / surface.normal.dot(this.v); // 4.7.4
} else { } 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(); const otherNormal = surface.normal.cross(curve.v)._normalize();
return otherNormal.dot(curve.p0.minus(this.p0)) / otherNormal.dot(this.v); // (4.8.3) 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) { point(t) {
@ -34,6 +44,9 @@ export class Line {
} }
pointOfSurfaceIntersection(surface) { pointOfSurfaceIntersection(surface) {
if (!this._pointsCache) {
this._pointsCache = new Map();
}
let point = this._pointsCache.get(surface); let point = this._pointsCache.get(surface);
if (!point) { if (!point) {
const t = this.intersectSurface(surface); const t = this.intersectSurface(surface);
@ -55,7 +68,7 @@ export class Line {
Line.prototype.isLine = true; Line.prototype.isLine = true;
Line.fromTwoPlanesIntersection = function(plane1, plane2) { Line.fromTwoPlanesIntersection = function(plane1, plane2): Line {
const n1 = plane1.normal; const n1 = plane1.normal;
const n2 = plane2.normal; const n2 = plane2.normal;
const v = n1.cross(n2)._normalize(); const v = n1.cross(n2)._normalize();

View file

@ -3,7 +3,18 @@ import {Line} from './line';
import {AXIS, BasisForPlane, Matrix3} from '../../../../../modules/math/l3space'; import {AXIS, BasisForPlane, Matrix3} from '../../../../../modules/math/l3space';
import {eqTol, veq} from '../tolerance'; import {eqTol, veq} from '../tolerance';
export class Plane { 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) { constructor(normal, w) {
this.normal = normal; this.normal = normal;
@ -15,14 +26,14 @@ export class Plane {
} }
basis() { basis() {
if (!this._basis) { if (!this.#basis) {
this._basis = this.calculateBasis(); this.#basis = this.calculateBasis();
} }
return this._basis; return this.#basis;
} }
intersectForSameClass(other) { intersectForSameClass(other) {
return new Line.fromTwoPlanesIntersection(this, other); return Line.fromTwoPlanesIntersection(this, other);
} }
translate(vector) { translate(vector) {
@ -34,21 +45,21 @@ export class Plane {
} }
get2DTransformation() { get2DTransformation() {
if (!this.__2dTr) { if (!this.#_2dTr) {
this.__2dTr = this.get3DTransformation().invert(); this.#_2dTr = this.get3DTransformation().invert();
} }
return this.__2dTr; return this.#_2dTr;
} }
get3DTransformation() { get3DTransformation() {
if (!this.__3dTr) { if (!this.#_3dTr) {
const basis = new Matrix3().setBasis(this.basis()); const basis = new Matrix3().setBasis(this.basis());
const translate = new Matrix3(); const translate = new Matrix3();
translate.tz = this.w; translate.tz = this.w;
this.__3dTr = basis.combine(translate); this.#_3dTr = basis.combine(translate);
// this.__3dTr.tz = this.w; // this.#_3dTr.tz = this.w;
} }
return this.__3dTr; return this.#_3dTr;
} }
coplanarUnsigned(other) { coplanarUnsigned(other) {
@ -61,11 +72,11 @@ export class Plane {
} }
toParametricForm() { toParametricForm() {
if (!this.__parametricForm) { if (!this.#parametricForm) {
const basis = BasisForPlane(this.normal); const [x, y, z] = BasisForPlane(this.normal);
this.__parametricForm = new ParametricPlane(this.normal.multiply(this.w), basis.x, basis.y); this.#parametricForm = new ParametricPlane(this.normal.multiply(this.w), x, y);
} }
return this.__parametricForm; return this.#parametricForm;
} }
toUV(point) { toUV(point) {
@ -96,12 +107,12 @@ export class Plane {
Plane.prototype.TYPE = 'plane'; Plane.prototype.TYPE = 'plane';
Plane.prototype.isPlane = true; 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 { class ParametricPlane {
r0: any;
r1: any;
r2: any;
constructor(r0, r1, r2) { constructor(r0, r1, r2) {
this.r0 = r0; this.r0 = r0;
this.r1 = r1; this.r1 = r1;
@ -112,3 +123,14 @@ class ParametricPlane {
return this.r0 + this.r1.multiply(u) + this.r2.multiply(v); return this.r0 + this.r1.multiply(u) + this.r2.multiply(v);
} }
} }
declare module './plane' {
interface Plane {
TYPE: string;
isPlane: boolean;
}
}

View file

@ -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<AlgNumConstraint> = 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;
}
}

View file

@ -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);
}

View file

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

View file

@ -1,13 +1,15 @@
import {ApplicationContext} from "context"; import {ApplicationContext} from "context";
import {ModellerContextualActions} from "./ui/ModellerContextualActions"; import {ModellerContextualActions} from "./ui/ModellerContextualActions";
import {state, StateStream} from "lstream"; import {state, StateStream} from "lstream";
import {AssemblyConstraintDefinition} from "./assemblyConstraintDefinition";
import {AssemblyProcess, launchAssembly} from "./assemblySolver"; import {AssemblyProcess, launchAssembly} from "./assemblySolver";
import {SolveStatus} from "../../sketcher/constr/AlgNumSystem"; import {SolveStatus} from "../../sketcher/constr/AlgNumSystem";
import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints"; import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints";
import {AssemblyView} from "./ui/AssemblyView"; import {AssemblyView} from "./ui/AssemblyView";
import {IoMdConstruct} from "react-icons/io"; import {IoMdConstruct} from "react-icons/io";
import {AssemblyConstraints, Constraints3D} from "./constraints3d"; import {AssemblyConstraintDefinition} from "./assemblyConstraint";
import {AssemblyConstraintsSchemas} from "./assemblySchemas";
export function activate(ctx: ApplicationContext) { export function activate(ctx: ApplicationContext) {
@ -20,7 +22,7 @@ export function activate(ctx: ApplicationContext) {
function loadConstraints(inData: AssemblyConstraintDefinition[]): void { function loadConstraints(inData: AssemblyConstraintDefinition[]): void {
inData = inData.filter(constr => { inData = inData.filter(constr => {
const shouldBeFiltered = !AssemblyConstraints[constr.typeId]; const shouldBeFiltered = !AssemblyConstraintsSchemas[constr.typeId];
if (shouldBeFiltered) { if (shouldBeFiltered) {
console.log('Unknown constraint ' + constr.typeId + ' will be skipped'); console.log('Unknown constraint ' + constr.typeId + ' will be skipped');
} }

View file

@ -0,0 +1,10 @@
import {AssemblyConstraintSchema} from "./assemblyConstraint";
import {FaceTouchAlign} from "./constraints/faceTouchAlign";
export const AssemblyConstraintsSchemas: {
[typeId: string]: AssemblyConstraintSchema
} = {
FaceTouchAlign
};

View file

@ -1,33 +1,30 @@
import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints"; import {SolveStatus} from "../../sketcher/constr/AlgNumSystem";
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 {MShell} from "../model/mshell"; 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 {MObject} from "../model/mobject";
import {CadRegistry} from "../craft/cadRegistryPlugin"; 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; assemblyDOF: AssemblyDOF;
relationships = new Map<RigidBody, AssemblyConstraint[]>();
reset() {
this.model.traverse(m => {
if (m.assemblyNodes) {
Object.values(m.assemblyNodes).forEach((node: AssemblyNode) => node.reset());
}
})
} }
} }
export class AssemblyConstraint { export interface RigidBody {
objects: MObject[] = [];
schema: AssemblyConstraintSchema; model: MShell;
constraints;
} }
export class AssemblyProcess { export class AssemblyProcess {
@ -39,37 +36,31 @@ export class AssemblyProcess {
error: 0 error: 0
}; };
errorStep = null; errorStep = null;
cadRegistry: CadRegistry;
constructor(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]) { constructor(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]) {
this.cadRegistry = cadRegistry;
this.queue = buildAssemblyQueue(cadRegistry, constraintDefs) this.queue = buildAssemblyQueue(cadRegistry, constraintDefs)
} }
begin() {
this.cadRegistry.getAllShells().forEach(s => {
s.location$.mutate(l => l.reset());
s.assemblyDOF = new SixDOF();
});
}
step() { step() {
const body = this.queue.pop(); const body = this.queue.pop();
const constraints = [];
body.relationships.forEach((overConstraints, bodyBuddy) => {
if (this.solved.has(bodyBuddy)) { this.solveStatus = solve(body.constraints, body.model, body.model.location);
overConstraints.forEach(c => constraints.push(c));
}
});
body.reset();
this.solveStatus = solve(constraints, body.model, true);
if (!this.solveStatus.success) { if (!this.solveStatus.success) {
this.errorStep = body.model.id; 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; return;
} }
// this.solveStatus = solve(constraints, body.model, false); (body.model as MShell).location$.next(body.model.location);
// 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);
this.solved.add(body); this.solved.add(body);
} }
@ -80,73 +71,145 @@ export class AssemblyProcess {
} }
function buildAssemblyQueue(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]): RigidBody[] { function buildAssemblyQueue(cadRegistry: CadRegistry, constraintDefs: AssemblyConstraintDefinition[]): RigidBody[] {
const constraints: AssemblyConstraint[] = []; const constraints: AssemblyConstraint[] = [];
const graph: Map<MObject, AssemblyConstraint[]> = 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 => { constraintDefs.forEach(def => {
const schema = AssemblyConstraints[def.typeId]; const schema = AssemblyConstraintsSchemas[def.typeId];
if (!schema) { if (!schema) {
console.error('reference to nonexistent constraint ' + def.typeId); console.error('reference to nonexistent constraint ' + def.typeId);
return null; return null;
} }
const constraint = new AssemblyConstraint();
constraint.schema = schema; const objects: MObject[] = [];
const objects = []; let movingPart: MObject = null;
let fixedPart: MObject = null;
for (const id of def.objects) { for (const id of def.objects) {
const modelObject = cadRegistry.find(id); const modelObject = cadRegistry.find(id);
if (!modelObject) { if (!modelObject) {
console.warn('skipping constraint referring to nonexistent object ' + id); console.warn('skipping constraint referring to nonexistent object ' + id);
return null; return null;
} }
constraint.objects.push(modelObject);
objects.push(modelObject); objects.push(modelObject);
}
constraints.push(constraint);
});
if (movingPart === null) {
const bodies = new Map<MObject, RigidBody>(); movingPart = modelObject.root;
function body(obj: MObject) { } else if (fixedPart === null) {
let rigidBody = bodies.get(obj); if (modelObject.root !== movingPart) {
if (!rigidBody) { fixedPart = modelObject.root;
rigidBody = new RigidBody(); }
rigidBody.model = obj; } else {
bodies.set(obj, rigidBody); console.error('constraint may only involve two parts or less, skipping ' + def.typeId);
} return null;
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<MObject>();
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);
} }
} }
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<MObject>();
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<MObject, RigidBody>();
// 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<MObject>();
// 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 { export function launchAssembly(assemblyProcess: AssemblyProcess): void {
assemblyProcess.begin();
while (!assemblyProcess.isDone()) { while (!assemblyProcess.isDone()) {
assemblyProcess.step(); assemblyProcess.step();
if (assemblyProcess.errorStep !== null) { if (assemblyProcess.errorStep !== null) {
@ -156,125 +219,42 @@ export function launchAssembly(assemblyProcess: AssemblyProcess): void {
} }
function addToStage(stage: ISolveStage, object: AssemblyNode) { function solve(constraints: AssemblyConstraint[], freeBody: MShell, location: Matrix3): SolveStatus {
stage.objects.add(object);
object.stage = stage;
}
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 = { return {
objects: new Set<AssemblyNode>(), success: true,
index: 0 error: 0
}; }
const stage: ISolveStage = {
objects: new Set<AssemblyNode>(),
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);
// });
} }
// 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
// }
//
// }

View file

@ -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
};

View file

@ -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,
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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])
];
}
}

View file

@ -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])
];
}
}

View file

@ -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])
];
}
}

View file

@ -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])];
}
}

View file

@ -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);
}
}

View file

@ -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 [
];
}
}

View file

@ -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])];
}
}

View file

@ -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])
];
}
}

View file

@ -1,17 +1,15 @@
import React, {useContext, useEffect, useState} from 'react'; import React, {useContext, useEffect} from 'react';
import {useStream} from "ui/effects"; import {useStream} from "ui/effects";
import {Status} from "ui/components/Status"; import {Status} from "ui/components/Status";
import Folder from "ui/components/Folder";
import {AssemblyConstraints, Constraints3D} from "../constraints3d";
import {AppContext} from "../../dom/components/AppContext"; import {AppContext} from "../../dom/components/AppContext";
import cx from 'classnames'; import cx from 'classnames';
import {NoIcon} from "../../../sketcher/icons/NoIcon"; import {NoIcon} from "../../../sketcher/icons/NoIcon";
import ls from "../../../sketcher/components/ConstraintExplorer.less"; import ls from "../../../sketcher/components/ConstraintExplorer.less";
import Fa from "ui/components/Fa"; import Fa from "ui/components/Fa";
import {AssemblyConstraintDefinition} from "../assemblyConstraintDefinition";
import {ApplicationContext} from "context"; import {ApplicationContext} from "context";
import {AssemblyProcess} from "../assemblySolver";
import {StepByStepSimulation} from "./StepByStepSimulation"; import {StepByStepSimulation} from "./StepByStepSimulation";
import {AssemblyConstraintDefinition} from "../assemblyConstraint";
import {AssemblyConstraintsSchemas} from "../assemblySchemas";
export function AssemblyView() { export function AssemblyView() {
@ -65,8 +63,8 @@ export function AssemblyConstraintButton({prefix='', constraint: c, ...props}: {
useEffect(() => withdraw, [c]); useEffect(() => withdraw, [c]);
const schema = AssemblyConstraints[c.typeId]; const schema = AssemblyConstraintsSchemas[c.typeId];
if (schema === null) { if (!schema) {
return <div className='warning-text'>Invalid Constraint {c.typeId} </div> return <div className='warning-text'>Invalid Constraint {c.typeId} </div>
} }

View file

@ -2,8 +2,9 @@ import React, {useContext} from 'react';
import {AppContext} from "../../dom/components/AppContext"; import {AppContext} from "../../dom/components/AppContext";
import {useStream} from "ui/effects"; import {useStream} from "ui/effects";
import {Dialog} from "ui/components/Dialog"; import {Dialog} from "ui/components/Dialog";
import {AssemblyConstraints, AssemblyConstraintSchema, Constraints3D} from "../constraints3d";
import {matchAvailableSubjects, MatchIndex, matchSelection} from "../../../sketcher/selectionMatcher"; import {matchAvailableSubjects, MatchIndex, matchSelection} from "../../../sketcher/selectionMatcher";
import {AssemblyConstraintSchema} from "../assemblyConstraint";
import {AssemblyConstraintsSchemas} from "../assemblySchemas";
export function ModellerContextualActions() { export function ModellerContextualActions() {
@ -17,7 +18,7 @@ export function ModellerContextualActions() {
const entities = selection.map(ctx.cadRegistry.find); 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[]; const availableConstraints = matchAvailableSubjects(entities, allConstraints) as AssemblyConstraintSchema[];
if (availableConstraints.length === 0) { if (availableConstraints.length === 0) {

View file

@ -17,12 +17,7 @@ export function StepByStepSimulation() {
function stepByStepSimulation() { function stepByStepSimulation() {
if (process === null || process.isDone()) { if (process === null || process.isDone()) {
const newProcess = new AssemblyProcess(ctx.cadRegistry, constraints); const newProcess = new AssemblyProcess(ctx.cadRegistry, constraints);
newProcess.queue.forEach(rb => { newProcess.begin();
if (rb.model.root instanceof MShell) {
rb.model.root.location$.next(new Matrix3());
}
});
newProcess.step();
setProcess(newProcess); setProcess(newProcess);
} else { } else {
process.step(); process.step();

View file

@ -1,12 +1,13 @@
import {MShell} from '../model/mshell'; import {MShell} from '../model/mshell';
import {MObject} from "../model/mobject"; import {MObject} from "../model/mobject";
import {ApplicationContext} from "context"; import {ApplicationContext} from "context";
import {Stream} from "lstream";
export function activate(ctx: ApplicationContext) { export function activate(ctx: ApplicationContext) {
const {streams, services} = ctx; const {streams, services} = ctx;
const shells$ = streams.craft.models.map(models => models.filter(m => m instanceof MShell)).remember(); const shells$: Stream<MShell> = streams.craft.models.map(models => models.filter(m => m instanceof MShell)).remember();
const modelIndex$ = streams.craft.models.map(models => { const modelIndex$ = streams.craft.models.map(models => {
const index = new Map(); const index = new Map();
models.forEach(model => model.traverse(m => index.set(m.id, m))); models.forEach(model => model.traverse(m => index.set(m.id, m)));
@ -21,7 +22,7 @@ export function activate(ctx: ApplicationContext) {
const index = () => modelIndex$.value; const index = () => modelIndex$.value;
function getAllShells() { function getAllShells(): MShell[] {
return streams.cadRegistry.shells.value; return streams.cadRegistry.shells.value;
} }
@ -79,7 +80,7 @@ export function activate(ctx: ApplicationContext) {
export interface CadRegistry { export interface CadRegistry {
getAllShells(): MObject[]; getAllShells(): MShell[];
findShell(id: string): MObject; findShell(id: string): MObject;
findFace(id: string): MObject; findFace(id: string): MObject;
findEdge(id: string): MObject; findEdge(id: string): MObject;

View file

@ -20,17 +20,17 @@ export function LocationDialog() {
const [location, setLocation] = useStreamWithUpdater(() => req ? req.shell.location$ : never<CSys>()); const [location, setLocation] = useStreamWithUpdater(() => req ? req.shell.location$ : never<CSys>());
const setX = useCallback(x => { const setX = useCallback(x => {
location.origin.x = parseFloat(x); location.tx = parseFloat(x);
setLocation(location); setLocation(location);
}, [setLocation]); }, [setLocation]);
const setY = useCallback(y => { const setY = useCallback(y => {
location.origin.y = parseFloat(y); location.ty = parseFloat(y);
setLocation(location); setLocation(location);
}, [setLocation]); }, [setLocation]);
const setZ = useCallback(z => { const setZ = useCallback(z => {
location.origin.z = parseFloat(z); location.tz = parseFloat(z);
setLocation(location); setLocation(location);
}, [setLocation]); }, [setLocation]);
// //
@ -81,15 +81,15 @@ export function LocationDialog() {
<Group> <Group>
<Field active={false} name='X'> <Field active={false} name='X'>
<Label>X:</Label> <Label>X:</Label>
<NumberControl onChange={setX} value={location.origin.x} /> <NumberControl onChange={setX} value={location.tx} />
</Field> </Field>
<Field active={false} name='Y'> <Field active={false} name='Y'>
<Label>Y:</Label> <Label>Y:</Label>
<NumberControl onChange={setY} value={location.origin.y} /> <NumberControl onChange={setY} value={location.ty} />
</Field> </Field>
<Field active={false} name='Z'> <Field active={false} name='Z'>
<Label>Z:</Label> <Label>Z:</Label>
<NumberControl onChange={setZ} value={location.origin.z} /> <NumberControl onChange={setZ} value={location.tz} />
</Field> </Field>
</Group> </Group>
</Folder> </Folder>

View file

@ -1,6 +1,7 @@
import {MObject, MObjectIdGenerator} from './mobject'; import {MObject, MObjectIdGenerator, MRootObject} from './mobject';
import CSys from "math/csys"; import CSys from "math/csys";
import Vector from "math/vector"; import Vector from "math/vector";
import {Matrix3} from "math/l3space";
export class MDatum extends MObject { export class MDatum extends MObject {

View file

@ -1,16 +1,13 @@
import {MObject} from './mobject'; import {MObject} from './mobject';
import Vector from 'math/vector'; import Vector from 'math/vector';
import {BasisForPlane} from 'math/l3space'; import {Basis, BasisForPlane} from 'math/l3space';
import {MSketchObject} from './msketchObject'; import {MSketchObject} from './msketchObject';
import {EMPTY_ARRAY} from 'gems/iterables'; import {EMPTY_ARRAY} from 'gems/iterables';
import CSys from 'math/csys'; import CSys from 'math/csys';
import {MSketchLoop} from './mloop'; import {MSketchLoop} from './mloop';
import {ProductionInfo} from './productionInfo'; import {ProductionInfo} from './productionInfo';
import {MBrepShell, MShell} from "./mshell"; import {MBrepShell, MShell} from "./mshell";
import {AssemblyUnitVectorNode} from "../assembly/nodes/assemblyUnitVectorNode"; import BBox from "../../math/bbox";
import {AssemblyScalarNode} from "../assembly/nodes/assemblyScalarNode";
import {AssemblyVectorNode} from "../assembly/nodes/assemblyVectorNode";
import {AssemblyPlaneNode} from "../assembly/nodes/assemblyPlaneNode";
export class MFace extends MObject { export class MFace extends MObject {
@ -22,12 +19,6 @@ export class MFace extends MObject {
sketch: any; sketch: any;
brepFace: any; brepFace: any;
assemblyNodes: {
normal: AssemblyUnitVectorNode
plane: AssemblyPlaneNode,
// w: AssemblyScalarNode
};
private _csys: any; private _csys: any;
private w: number; private w: number;
private _basis: [Vector, Vector, Vector]; private _basis: [Vector, Vector, Vector];
@ -42,11 +33,6 @@ export class MFace extends MObject {
this.sketchObjects = []; this.sketchObjects = [];
this.sketchLoops = []; this.sketchLoops = [];
this._csys = csys; 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 { normal(): Vector {
@ -58,19 +44,19 @@ export class MFace extends MObject {
return this.w; return this.w;
} }
basis() { basis(): Basis {
if (!this._basis) { if (!this._basis) {
this._basis = [this.csys.x, this.csys.y, this.csys.z]; this._basis = [this.csys.x, this.csys.y, this.csys.z];
} }
return this._basis; return this._basis;
} }
get csys() { get csys(): CSys {
this.evalCSys(); this.evalCSys();
return this._csys; return this._csys;
} }
get isPlaneBased() { get isPlaneBased(): boolean {
return this.surface.simpleSurface && this.surface.simpleSurface.isPlane; return this.surface.simpleSurface && this.surface.simpleSurface.isPlane;
} }
@ -185,10 +171,15 @@ export class MFace extends MObject {
return this.shell; return this.shell;
} }
get favorablePoint() {
return this.csys.origin;
}
} }
export class MBrepFace extends MFace { export class MBrepFace extends MFace {
#favorablePoint: Vector;
constructor(id, shell, brepFace) { constructor(id, shell, brepFace) {
super(id, shell, brepFace.surface); super(id, shell, brepFace.surface);
this.id = id; this.id = id;
@ -213,4 +204,21 @@ export class MBrepFace extends MFace {
} }
return bounds; 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;
}
} }

View file

@ -1,4 +1,5 @@
import {AssemblyNode} from "../assembly/assembly"; import {AssemblyNode} from "../assembly/assembly";
import {IDENTITY_MATRIX, Matrix3} from "math/l3space";
export abstract class MObject { export abstract class MObject {
@ -29,6 +30,10 @@ export abstract class MObject {
} }
return obj; return obj;
} }
get location() {
return IDENTITY_MATRIX;
}
} }
export const MObjectIdGenerator = { export const MObjectIdGenerator = {

View file

@ -5,11 +5,6 @@ import {MVertex} from './mvertex';
import CSys from 'math/csys'; import CSys from 'math/csys';
import {Matrix3} from "math/l3space"; import {Matrix3} from "math/l3space";
import {state, StateStream} from "lstream"; 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 { export class MShell extends MObject {
@ -18,26 +13,14 @@ export class MShell extends MObject {
csys: CSys; csys: CSys;
shell; shell;
faces = []; faces = [];
edges = []; edges = [];
vertices = []; vertices = [];
location$: StateStream<Matrix3> = state(new Matrix3()); location$: StateStream<Matrix3> = state(new Matrix3());
assemblyNodes: {
location: AssemblyLocationNode,
orientation: AssemblyOrientationNode,
translation: AssemblyTranslationNode,
};
constructor() { constructor() {
super(MShell.TYPE, MObjectIdGenerator.next(MShell.TYPE, 'S')); 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 { traverse(callback: (obj: MObject) => void): void {
@ -50,6 +33,10 @@ export class MShell extends MObject {
get parent() { get parent() {
return null; return null;
} }
get location() {
return this.location$.value;
}
} }
export class MBrepShell extends MShell { export class MBrepShell extends MShell {

View file

@ -1,4 +1,14 @@
export interface SelectionMatcher {
selector: string,
types: any[],
minQuantity: number
}
export function matchAvailableSubjects(selection, subjects) { export function matchAvailableSubjects(selection, subjects) {
let matched = []; let matched = [];
@ -95,6 +105,8 @@ export class MatchIndex {
typeMap = new Map(); typeMap = new Map();
overallHits = 0; overallHits = 0;
result: any[];
selection: any;
constructor(selection) { constructor(selection) {
this.selection = selection; this.selection = selection;

View file

@ -4,7 +4,7 @@ import {Styles} from "../styles";
import {NoIcon} from "../icons/NoIcon"; import {NoIcon} from "../icons/NoIcon";
import {Layer, Viewer} from "../viewer2d"; import {Layer, Viewer} from "../viewer2d";
import {NOOP} from "gems/func"; import {NOOP} from "gems/func";
import {SolvableObject} from "../constr/solveObject"; import {SolvableObject} from "../constr/solvableObject";
export abstract class SketchObject extends Shape implements SolvableObject { export abstract class SketchObject extends Shape implements SolvableObject {