mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +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 = [];
|
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) {
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -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/*",
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {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');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 {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
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
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 {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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue