mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
basic DOFs for assemblies
This commit is contained in:
parent
c9ac719f83
commit
801480c5f4
37 changed files with 721 additions and 1410 deletions
|
|
@ -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 = [];
|
||||
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<T>(node:T,
|
||||
children: (node: T, consumer: (node: T) => void) => void,
|
||||
callback: (node) => any): boolean {
|
||||
|
||||
const visited = new Set<T>();
|
||||
const queue = [];
|
||||
queue.unshift(node);
|
||||
while (queue.length) {
|
||||
|
|
@ -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};
|
||||
|
|
@ -6,9 +6,10 @@
|
|||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES5",
|
||||
"target": "ES2015",
|
||||
"baseUrl": ".",
|
||||
"downlevelIteration": true,
|
||||
"module": "commonJS",
|
||||
"paths": {
|
||||
"*": [
|
||||
"modules/*",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import Vector from "math/vector";
|
||||
|
||||
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) {
|
||||
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();
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
56
web/app/cad/assembly/assemblyConstraint.ts
Normal file
56
web/app/cad/assembly/assemblyConstraint.ts
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import {ConstantsDefinitions} from "../../sketcher/constr/ANConstraints";
|
||||
|
||||
export interface AssemblyConstraintDefinition {
|
||||
|
||||
typeId: string;
|
||||
|
||||
objects: string[];
|
||||
|
||||
constants: ConstantsDefinitions
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
10
web/app/cad/assembly/assemblySchemas.ts
Normal file
10
web/app/cad/assembly/assemblySchemas.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {AssemblyConstraintSchema} from "./assemblyConstraint";
|
||||
import {FaceTouchAlign} from "./constraints/faceTouchAlign";
|
||||
|
||||
export const AssemblyConstraintsSchemas: {
|
||||
[typeId: string]: AssemblyConstraintSchema
|
||||
} = {
|
||||
|
||||
FaceTouchAlign
|
||||
|
||||
};
|
||||
|
|
@ -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<RigidBody, AssemblyConstraint[]>();
|
||||
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<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 => {
|
||||
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<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);
|
||||
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<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 {
|
||||
|
||||
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<AssemblyNode>(),
|
||||
index: 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);
|
||||
// });
|
||||
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
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
|
|
|||
40
web/app/cad/assembly/constraints/faceTouchAlign.ts
Normal file
40
web/app/cad/assembly/constraints/faceTouchAlign.ts
Normal 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
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -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,
|
||||
|
||||
}
|
||||
|
||||
93
web/app/cad/assembly/dof/PPDOF.ts
Normal file
93
web/app/cad/assembly/dof/PPDOF.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
31
web/app/cad/assembly/dof/PPPPDOF.ts
Normal file
31
web/app/cad/assembly/dof/PPPPDOF.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
23
web/app/cad/assembly/dof/assemblyDOF.ts
Normal file
23
web/app/cad/assembly/dof/assemblyDOF.ts
Normal 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;
|
||||
|
||||
|
||||
}
|
||||
33
web/app/cad/assembly/dof/conflictDOF.ts
Normal file
33
web/app/cad/assembly/dof/conflictDOF.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
39
web/app/cad/assembly/dof/sixDOF.ts
Normal file
39
web/app/cad/assembly/dof/sixDOF.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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])
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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])
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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])
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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])];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 [
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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])];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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])
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <div className='warning-text'>Invalid Constraint {c.typeId} </div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<MShell> = 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;
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@ export function LocationDialog() {
|
|||
const [location, setLocation] = useStreamWithUpdater(() => req ? req.shell.location$ : never<CSys>());
|
||||
|
||||
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() {
|
|||
<Group>
|
||||
<Field active={false} name='X'>
|
||||
<Label>X:</Label>
|
||||
<NumberControl onChange={setX} value={location.origin.x} />
|
||||
<NumberControl onChange={setX} value={location.tx} />
|
||||
</Field>
|
||||
<Field active={false} name='Y'>
|
||||
<Label>Y:</Label>
|
||||
<NumberControl onChange={setY} value={location.origin.y} />
|
||||
<NumberControl onChange={setY} value={location.ty} />
|
||||
</Field>
|
||||
<Field active={false} name='Z'>
|
||||
<Label>Z:</Label>
|
||||
<NumberControl onChange={setZ} value={location.origin.z} />
|
||||
<NumberControl onChange={setZ} value={location.tz} />
|
||||
</Field>
|
||||
</Group>
|
||||
</Folder>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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<Matrix3> = 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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue