diff --git a/modules/math/l3space.ts b/modules/math/l3space.ts index 878a85d5..914935ad 100644 --- a/modules/math/l3space.ts +++ b/modules/math/l3space.ts @@ -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(); diff --git a/modules/math/vector.ts b/modules/math/vector.ts index 76937c71..b469625c 100644 --- a/modules/math/vector.ts +++ b/modules/math/vector.ts @@ -1,3 +1,4 @@ +import {clamp} from "../../web/app/math/math"; export default class Vector { @@ -154,6 +155,12 @@ export default class Vector { data[1] = this.y; 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); diff --git a/web/app/cad/assembly/assemblySchemas.ts b/web/app/cad/assembly/assemblySchemas.ts index fde9e1e5..facc31d3 100644 --- a/web/app/cad/assembly/assemblySchemas.ts +++ b/web/app/cad/assembly/assemblySchemas.ts @@ -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 }; diff --git a/web/app/cad/assembly/constraints/edgeAlign.ts b/web/app/cad/assembly/constraints/edgeAlign.ts new file mode 100644 index 00000000..68ef4be3 --- /dev/null +++ b/web/app/cad/assembly/constraints/edgeAlign.ts @@ -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 + + +}; \ No newline at end of file diff --git a/web/app/cad/assembly/constraints3d.ts b/web/app/cad/assembly/constraints3d.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/web/app/cad/assembly/dof/EEDOF.ts b/web/app/cad/assembly/dof/EEDOF.ts new file mode 100644 index 00000000..2a77709d --- /dev/null +++ b/web/app/cad/assembly/dof/EEDOF.ts @@ -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); + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/EEEEDOF.ts b/web/app/cad/assembly/dof/EEEEDOF.ts new file mode 100644 index 00000000..89c1e7f1 --- /dev/null +++ b/web/app/cad/assembly/dof/EEEEDOF.ts @@ -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; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/PPDOF.ts b/web/app/cad/assembly/dof/PPDOF.ts index 8986312c..fff8b379 100644 --- a/web/app/cad/assembly/dof/PPDOF.ts +++ b/web/app/cad/assembly/dof/PPDOF.ts @@ -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,50 +19,6 @@ export class PPDOF implements AssemblyDOF { 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; } @@ -90,4 +46,54 @@ export class PPDOF implements AssemblyDOF { return illegalTranslation ? ModificationResponse.FIXED : ModificationResponse.SUCCESS; } + 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 new PPPPDOF(this.plane, new Plane(vecB.copy(), vecB.dot(ptFixed))); + } + + applyEdgeAlign(constr: EdgeAlignConstraint): AssemblyDOF { + + + + return this; + } + } \ No newline at end of file diff --git a/web/app/cad/assembly/dof/PPEEDOF.ts b/web/app/cad/assembly/dof/PPEEDOF.ts new file mode 100644 index 00000000..fed55ed7 --- /dev/null +++ b/web/app/cad/assembly/dof/PPEEDOF.ts @@ -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; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/dof/PPPPDOF.ts b/web/app/cad/assembly/dof/PPPPDOF.ts index 450ce2fe..2d609a19 100644 --- a/web/app/cad/assembly/dof/PPPPDOF.ts +++ b/web/app/cad/assembly/dof/PPPPDOF.ts @@ -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; + } + } \ No newline at end of file diff --git a/web/app/cad/assembly/dof/assemblyDOF.ts b/web/app/cad/assembly/dof/assemblyDOF.ts index 9e9ba0f5..ab42acb2 100644 --- a/web/app/cad/assembly/dof/assemblyDOF.ts +++ b/web/app/cad/assembly/dof/assemblyDOF.ts @@ -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; } \ No newline at end of file diff --git a/web/app/cad/assembly/dof/conflictDOF.ts b/web/app/cad/assembly/dof/conflictDOF.ts index 7c75a1b9..e9eda224 100644 --- a/web/app/cad/assembly/dof/conflictDOF.ts +++ b/web/app/cad/assembly/dof/conflictDOF.ts @@ -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; + } + } \ No newline at end of file diff --git a/web/app/cad/assembly/dof/sixDOF.ts b/web/app/cad/assembly/dof/sixDOF.ts index fa572e2f..cf027653 100644 --- a/web/app/cad/assembly/dof/sixDOF.ts +++ b/web/app/cad/assembly/dof/sixDOF.ts @@ -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()); - translate(dir: Vector, location: Matrix3, strict: boolean): ModificationResponse { - return ModificationResponse.REJECTED; + if (vecA.dot(vecB)< 0) { + vecB._negate(); + } + + 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); } } \ No newline at end of file diff --git a/web/app/cad/craft/e0/operationHandler.js b/web/app/cad/craft/e0/operationHandler.js index 8162d4d9..e2e028c7 100644 --- a/web/app/cad/craft/e0/operationHandler.js +++ b/web/app/cad/craft/e0/operationHandler.js @@ -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)) diff --git a/web/app/cad/model/medge.ts b/web/app/cad/model/medge.ts index b2fa0dcf..38509f3d 100644 --- a/web/app/cad/model/medge.ts +++ b/web/app/cad/model/medge.ts @@ -26,6 +26,10 @@ export class MEdge extends MObject { return out; } + get favorablePoint() { + return this.brepEdge.curve.middlePoint(); + } + get parent() { return this.shell; }