edge aligns DOFs for assemblies

This commit is contained in:
Val Erastov (xibyte) 2020-07-05 00:32:14 -07:00
parent 801480c5f4
commit ef8a57ade3
15 changed files with 348 additions and 68 deletions

View file

@ -361,13 +361,13 @@ export class Matrix3 {
return Matrix3.rotateMatrix(angle, axis, pivot, this);
};
static rotateMatrix(angle: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 {
static rotateMatrix(angle: number, axis: Vector, pivot: Vector, matrix?: Matrix3): Matrix3 {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
return Matrix3.rotationMatrix(cos, sin, axis, pivot, matrix);
}
static rotationFromVectorToVector(from: Vector, to: Vector, pivot: Vector, matrix: Matrix3): Matrix3 {
static rotationFromVectorToVector(from: Vector, to: Vector, pivot: Vector, matrix?: Matrix3): Matrix3 {
const axis = from.cross(to);
@ -378,7 +378,7 @@ export class Matrix3 {
}
static rotationMatrix(cos: number, sin: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 {
static rotationMatrix(cos: number, sin: number, axis: Vector, pivot: Vector, matrix?: Matrix3): Matrix3 {
var axisX, axisY, axisZ;
var m = matrix || new Matrix3();

View file

@ -1,3 +1,4 @@
import {clamp} from "../../web/app/math/math";
export default class Vector {
@ -155,6 +156,12 @@ export default class Vector {
data[2] = this.z;
}
angleBetween(vecB: Vector) {
const cosA = clamp(this.dot(vecB), -1, 1);
const sinA = clamp(this.cross(vecB).length(), -1, 1);
return Math.atan2(sinA, cosA);
}
static fromData(arr: [number, number, number]): Vector {
return new Vector().set3(arr);
}

View file

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

View file

@ -0,0 +1,41 @@
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";
import {MEdge} from "../../model/medge";
export class EdgeAlignConstraint extends AssemblyConstraint {
fixedEdge: MEdge;
movingEdge: MEdge;
constructor(schema: AssemblyConstraintSchema, fixedPart: MShell, movingPart: MShell, objects: MObject[]) {
super(schema, fixedPart, movingPart, objects);
this.movingEdge = objects[0] as MEdge;
this.fixedEdge = objects[1] as MEdge;
}
apply(dof: AssemblyDOF) {
return dof.applyEdgeAlign(this);
}
}
export const EdgeAlign : AssemblyConstraintSchema = {
id: 'EdgeAlign',
name: 'Edge Align',
icon: NoIcon,
selectionMatcher: {
selector: 'matchAll',
types: ['edge'],
minQuantity: 2
},
implementation: EdgeAlignConstraint
};

View file

@ -0,0 +1,104 @@
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";
import {PPPPDOF} from "./PPPPDOF";
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
import {PPEEDOF} from "./PPEEDOF";
import {EEEEDOF} from "./EEEEDOF";
const ANGULAR_ALLOWANCE = 10 * DEG_RAD;
export class EEDOF implements AssemblyDOF {
description = 'edge to edge';
origin: Vector;
dir: Vector;
constructor(origin: Vector, dir: Vector) {
this.origin = origin;
this.dir = dir;
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
const rotationAxis = this.dir;
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 locking edge');
}
const location = constr.movingPart.root.location;
const rot = new Matrix3();
Matrix3.rotationFromVectorToVector(vecA, vecB, this.origin, rot);
rot.combine(location, location);
return new PPEEDOF(null, null);
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
const vecA = constr.movingPart.location.applyNoTranslation(constr.movingEdge.brepEdge.halfEdge1.tangentAtStart());
const vecB = constr.fixedPart.location.applyNoTranslation(constr.fixedEdge.brepEdge.halfEdge1.tangentAtStart());
if (vecA.dot(vecB)< 0) {
vecB._negate();
}
const rotationAxis = this.dir;
const angle1 = rotationAxis.angleBetween(vecA);
const angle2 = rotationAxis.angleBetween(vecB);
if (!areEqual(angle1, angle2, ANGULAR_ALLOWANCE)) {
console.log('constraint conflict');
return new ConflictDOF(constr, 'unable to align not matching angles edges with respect to locking edge');
}
const location = constr.movingPart.root.location;
const rot = new Matrix3();
Matrix3.rotationFromVectorToVector(vecA, vecB, this.origin, rot);
rot.combine(location, location);
const ptFixed = constr.fixedPart.location.apply(constr.fixedEdge.favorablePoint);
const ptMoving = constr.movingPart.location.apply(constr.movingEdge.favorablePoint);
const dir = ptFixed._minus(ptMoving);
// constr.movingPart.location.translateVec(dir);
return new EEEEDOF(this.origin, this.dir, constr.fixedEdge.favorablePoint, vecB);
}
}

View file

@ -0,0 +1,41 @@
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';
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
export class EEEEDOF implements AssemblyDOF {
description = 'edge to edge twice';
origin1: Vector;
dir1: Vector;
origin2: Vector;
dir2: Vector;
constructor(origin1: Vector, dir1: Vector, origin2: Vector, dir2: Vector) {
this.origin1 = origin1;
this.dir1 = dir1;
this.origin2 = origin2;
this.dir2 = dir2;
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
return new ConflictDOF(constr, 'plane touch/align constraint cannot be applied when object is at ' + this.description + ' relationship');
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
return this;
}
}

View file

@ -3,11 +3,11 @@ 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 {ANGULAR_ALLOWANCE, AssemblyDOF, ModificationResponse} from "./assemblyDOF";
import {areEqual, clamp, DEG_RAD} from "../../../math/math";
import {ConflictDOF} from "./conflictDOF";
const ANGULAR_ALLOWANCE = 10 * DEG_RAD;
import {PPPPDOF} from "./PPPPDOF";
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
export class PPDOF implements AssemblyDOF {
@ -19,6 +19,32 @@ export class PPDOF implements AssemblyDOF {
this.plane = plane;
}
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;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
@ -60,34 +86,14 @@ export class PPDOF implements AssemblyDOF {
location.translateVec(dir);
return new PPPPDOF(this.plane, new Plane(vecB.copy(), vecB.dot(ptFixed)));
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
return this;
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
const normal = this.plane.normal;
const illegalTranslation = !eqTol(normal.dot(dir), 0);
if (illegalTranslation && strict) {
return ModificationResponse.REJECTED;
}
//fix it anyway to mitigate any rounding errors
debugger;
const y = normal.cross(dir)._normalize();
const x = y.cross(normal)._normalize();
const u = x.dot(dir);
const fixedDir = x._multiply(u);
location.translateVec(fixedDir);
return illegalTranslation ? ModificationResponse.FIXED : ModificationResponse.SUCCESS;
}
}

View file

@ -0,0 +1,37 @@
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';
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
export class PPEEDOF implements AssemblyDOF {
plane1: Plane;
plane2: Plane;
description = 'plane to plane twice';
constructor(plane1: Plane, plane2: Plane) {
this.plane1 = plane1;
this.plane2 = plane2;
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
return new ConflictDOF(constr, 'plane touch/align constraint cannot be applied when object is at ' + this.description + ' relationship');
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
return this;
}
}

View file

@ -4,6 +4,7 @@ import { FaceTouchAlignConstraint } from "../constraints/faceTouchAlign";
import { Plane } from './../../../brep/geom/impl/plane';
import { AssemblyDOF, ModificationResponse } from "./assemblyDOF";
import { ConflictDOF } from './conflictDOF';
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
export class PPPPDOF implements AssemblyDOF {
@ -16,10 +17,6 @@ export class PPPPDOF implements AssemblyDOF {
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;
}
@ -28,4 +25,13 @@ export class PPPPDOF implements AssemblyDOF {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
return new ConflictDOF(constr, 'plane touch/align constraint cannot be applied when object is at ' + this.description + ' relationship');
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
return this;
}
}

View file

@ -1,7 +1,10 @@
import Vector from "math/vector";
import {Matrix3} from "math/l3space";
import {FaceTouchAlignConstraint} from "../constraints/faceTouchAlign";
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
import {DEG_RAD} from "../../../math/math";
export const ANGULAR_ALLOWANCE = 10 * DEG_RAD;
export enum ModificationResponse {
@ -13,11 +16,12 @@ 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;
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF;
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF;
}

View file

@ -4,6 +4,7 @@ import Vector from "math/vector";
import { AssemblyConstraint } from '../assemblyConstraint';
import { FaceTouchAlignConstraint } from "../constraints/faceTouchAlign";
import { AssemblyDOF, ModificationResponse } from "./assemblyDOF";
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
export class ConflictDOF implements AssemblyDOF {
@ -17,11 +18,6 @@ export class ConflictDOF implements AssemblyDOF {
this.infoMessage = infoMessage;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
return this;
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
@ -30,4 +26,12 @@ export class ConflictDOF implements AssemblyDOF {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
return this;
}
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
return this;
}
}

View file

@ -4,11 +4,21 @@ import Vector from "math/vector";
import {Matrix3, ORIGIN} from "math/l3space";
import {FaceTouchAlignConstraint} from "../constraints/faceTouchAlign";
import {PPDOF} from "./PPDOF";
import {EdgeAlignConstraint} from "../constraints/edgeAlign";
import {EEDOF} from "./EEDOF";
export class SixDOF implements AssemblyDOF {
description = 'full freedom';
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
}
applyTouchAlign(constr: FaceTouchAlignConstraint): AssemblyDOF {
const vecA = constr.movingPart.location.applyNoTranslation(constr.movingFace.normal());
@ -28,12 +38,27 @@ export class SixDOF implements AssemblyDOF {
return new PPDOF(new Plane(vecB.copy(), vecB.dot(ptFixed)));
}
rotate(axis: Vector, angle: number, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF {
const vecA = constr.movingPart.location.applyNoTranslation(constr.movingEdge.brepEdge.halfEdge1.tangentAtStart());
const vecB = constr.fixedPart.location.applyNoTranslation(constr.fixedEdge.brepEdge.halfEdge1.tangentAtStart());
if (vecA.dot(vecB)< 0) {
vecB._negate();
}
translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse {
return ModificationResponse.REJECTED;
const location = constr.movingPart.root.location;
Matrix3.rotationFromVectorToVector(vecA, vecB, ORIGIN, location);
const ptFixed = constr.fixedPart.location.apply(constr.fixedEdge.favorablePoint);
const ptMoving = constr.movingPart.location.apply(constr.movingEdge.favorablePoint);
const dir = ptFixed._minus(ptMoving);
location.translateVec(dir);
return new EEDOF(constr.fixedEdge.favorablePoint, vecB);
}
}

View file

@ -62,7 +62,7 @@ function createRevolveCommand(request, {cadRegistry, sketchStorageService}) {
let pivot = cadRegistry.findSketchObject(request.axis).sketchPrimitive;
let tr = face.csys.outTransformation;
let vec = __CAD_APP.services.exposure.math.vec;
let vec = __CAD_APP.services.exposure.math.dir;
let axisOrigin = tr._apply3(pivot.a.data());
let axisDir = vec._normalize(vec._sub(tr._apply3(pivot.b.data()), axisOrigin))

View file

@ -26,6 +26,10 @@ export class MEdge extends MObject {
return out;
}
get favorablePoint() {
return this.brepEdge.curve.middlePoint();
}
get parent() {
return this.shell;
}