@bitbybit-dev/occt
Version:
Bit By Bit Developers CAD algorithms using OpenCascade Technology kernel. Run in Node and in Browser.
790 lines (789 loc) • 38 kB
JavaScript
import * as Inputs from "../../api/inputs";
export class EdgesService {
constructor(occ, occRefReturns, shapeGettersService, entitiesService, iteratorService, converterService, enumService, geomService, transformsService, vecHelper) {
this.occ = occ;
this.occRefReturns = occRefReturns;
this.shapeGettersService = shapeGettersService;
this.entitiesService = entitiesService;
this.iteratorService = iteratorService;
this.converterService = converterService;
this.enumService = enumService;
this.geomService = geomService;
this.transformsService = transformsService;
this.vecHelper = vecHelper;
}
getCornerPointsOfEdgesForShape(inputs) {
const edges = this.shapeGettersService.getEdges(inputs);
let points = [];
edges.forEach((edge) => {
// Use the new API: GetEdgeCurve returns a Handle_Geom_Curve wrapper
// which already has Value() method - no need to call .get()
const crvHandle = this.occ.GetEdgeCurve(edge);
try {
// Check if handle is null using IsNull() method
if (crvHandle && !crvHandle.IsNull()) {
// Get parameter bounds from the edge
const edgeParams = this.occ.BRep_Tool_GetEdgeParameters(edge);
if (edgeParams.IsValid) {
// Value() is directly on the handle wrapper
const pt1 = crvHandle.Value(edgeParams.First);
const pt2 = crvHandle.Value(edgeParams.Last);
const pt1g = [pt1.X(), pt1.Y(), pt1.Z()];
const pt2g = [pt2.X(), pt2.Y(), pt2.Z()];
pt1.delete();
pt2.delete();
points.push(pt1g);
points.push(pt2g);
}
crvHandle.delete();
}
}
catch (ex) {
console.log(ex);
}
});
if (points.length > 0) {
points = this.vecHelper.removeAllDuplicateVectors(points);
}
return points;
}
getCircularEdgesAlongWire(inputs) {
return this.getEdgesAlongWire(inputs).filter(edge => this.isEdgeCircular({ shape: edge }));
}
getLinearEdgesAlongWire(inputs) {
return this.getEdgesAlongWire(inputs).filter(edge => this.isEdgeLinear({ shape: edge }));
}
getEdgesAlongWire(inputs) {
if (inputs.shape && this.enumService.getShapeTypeEnum(inputs.shape) === Inputs.OCCT.shapeTypeEnum.edge) {
return [inputs.shape];
}
if (!inputs.shape || inputs.shape.IsNull()) {
throw (new Error("Shape is not provided or is of incorrect type"));
}
const edges = [];
const wireWithFixedEdges = this.fixEdgeOrientationsAlongWire(inputs);
this.iteratorService.forEachEdgeAlongWire(wireWithFixedEdges, (i, edge) => {
edges.push(edge);
});
return edges;
}
fixEdgeOrientationsAlongWire(inputs) {
const edges = [];
this.iteratorService.forEachEdgeAlongWire(inputs.shape, (i, edge) => {
edges.push(edge);
});
// rebuilding wire from edges along wire fixes edge directions
return this.converterService.combineEdgesAndWiresIntoAWire({ shapes: edges });
}
arcThroughThreePoints(inputs) {
const gpPnt1 = this.entitiesService.gpPnt(inputs.start);
const gpPnt2 = this.entitiesService.gpPnt(inputs.middle);
const gpPnt3 = this.entitiesService.gpPnt(inputs.end);
const shape = this.occ.MakeArcThrough3Points(gpPnt1, gpPnt2, gpPnt3);
gpPnt1.delete();
gpPnt2.delete();
gpPnt3.delete();
return shape;
}
arcThroughTwoPointsAndTangent(inputs) {
const gpPnt1 = this.entitiesService.gpPnt(inputs.start);
const gpVec = this.entitiesService.gpVec(inputs.tangentVec);
const gpPnt2 = this.entitiesService.gpPnt(inputs.end);
const shape = this.occ.MakeArcWithTangent(gpPnt1, gpVec, gpPnt2);
gpPnt1.delete();
gpVec.delete();
gpPnt2.delete();
return shape;
}
arcFromCircleAndTwoAngles(inputs) {
const circle = this.getGpCircleFromEdge({ shape: inputs.circle });
const radAlpha1 = this.vecHelper.degToRad(inputs.alphaAngle1);
const radAlpha2 = this.vecHelper.degToRad(inputs.alphaAngle2);
const shape = this.occ.MakeArcOnCircleByAngles(circle, radAlpha1, radAlpha2, inputs.sense);
circle.delete();
return shape;
}
arcFromCirclePointAndAngle(inputs) {
const circle = this.getGpCircleFromEdge({ shape: inputs.circle });
const radAlpha = this.vecHelper.degToRad(inputs.alphaAngle);
const point = this.entitiesService.gpPnt(inputs.point);
const shape = this.occ.MakeArcOnCircleByAngle(circle, point, radAlpha, inputs.sense);
circle.delete();
point.delete();
return shape;
}
lineEdge(inputs) {
const gpPnt1 = this.entitiesService.gpPnt(inputs.start);
const gpPnt2 = this.entitiesService.gpPnt(inputs.end);
const edge = this.occ.MakeLineEdgeBetweenPoints(gpPnt1, gpPnt2);
gpPnt1.delete();
gpPnt2.delete();
return edge;
}
getEdgeLength(inputs) {
return this.occ.GetEdgeLength(inputs.shape);
}
getEdgeLengthsOfShape(inputs) {
const edgesOnShape = this.shapeGettersService.getEdges({ shape: inputs.shape });
return edgesOnShape.map(edge => {
return this.getEdgeLength({ shape: edge });
});
}
getEdgesLengths(inputs) {
if (inputs.shapes === undefined) {
throw (Error(("Shapes are not defined")));
}
return inputs.shapes.map(edge => this.getEdgeLength({ shape: edge }));
}
getEdgesCentersOfMass(inputs) {
if (inputs.shapes === undefined) {
throw (Error(("Shapes are not defined")));
}
return inputs.shapes.map(edge => this.geomService.getLinearCenterOfMass({ shape: edge }));
}
edgesToPoints(inputs) {
const shapeType = this.enumService.getShapeTypeEnum(inputs.shape);
let edges = [];
if (shapeType === Inputs.OCCT.shapeTypeEnum.edge) {
edges = [inputs.shape];
}
else if (shapeType === Inputs.OCCT.shapeTypeEnum.wire) {
edges = this.getEdgesAlongWire({ shape: inputs.shape });
}
else {
edges = this.shapeGettersService.getEdges({ shape: inputs.shape });
}
// Reuse edgeToPoints for each edge to ensure consistent direction handling
const allEdgePoints = edges.map(edge => {
return this.edgeToPoints(Object.assign(Object.assign({}, inputs), { shape: edge }));
});
return allEdgePoints;
}
startPointOnEdge(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const pt = this.geomService.pointOnCurveAtParam({ shape: curve, param: 0 });
curve.delete();
return pt;
}
endPointOnEdge(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const pt = this.geomService.pointOnCurveAtParam({ shape: curve, param: 1 });
curve.delete();
return pt;
}
edgeToPoints(inputs) {
const edgePoints = [];
const aLocation = new this.occ.TopLoc_Location();
const adaptorCurve = new this.occ.BRepAdaptor_Curve(inputs.shape);
const tangDef = new this.occ.GCPnts_TangentialDeflection(adaptorCurve, inputs.angularDeflection, inputs.curvatureDeflection, inputs.minimumOfPoints, inputs.uTolerance, inputs.minimumLength);
const nrPoints = tangDef.NbPoints();
const tangDefValues = [];
for (let j = 0; j < nrPoints; j++) {
const tangDefVal = tangDef.Value(j + 1);
edgePoints.push([
tangDefVal.X(),
tangDefVal.Y(),
tangDefVal.Z()
]);
tangDefValues.push(tangDefVal);
}
tangDefValues.forEach(v => v.delete());
aLocation.delete();
adaptorCurve.delete();
tangDef.delete();
// Ensure tessellation matches edge direction
// The tessellation might be in reverse order relative to the edge's start->end direction
if (edgePoints.length > 1) {
const edgeStart = this.startPointOnEdge({ shape: inputs.shape });
const tessStart = edgePoints[0];
const tessEnd = edgePoints[edgePoints.length - 1];
// Check if first tessellation point is closer to edge start or end
const distStartToStart = this.vecHelper.distanceBetweenPoints(tessStart, edgeStart);
const distEndToStart = this.vecHelper.distanceBetweenPoints(tessEnd, edgeStart);
// If the last tessellation point is closer to the edge start, the array is reversed
if (distEndToStart < distStartToStart) {
edgePoints.reverse();
}
}
return edgePoints;
}
makeEdgeFromGeom2dCurveAndSurfaceBounded(inputs, umin, umax) {
return this.occ.MakeEdgeFromGeom2dCurveAndSurfaceBounded(inputs.curve, inputs.surface, umin, umax);
}
makeEdgeFromGeom2dCurveAndSurface(inputs) {
return this.occ.MakeEdgeFromGeom2dCurveAndSurface(inputs.curve, inputs.surface);
}
constraintTanLinesFromTwoPtsToCircle(inputs) {
const cirDir = this.getCircularEdgePlaneDirection({ shape: inputs.circle });
const cirPos = this.getCircularEdgeCenterPoint({ shape: inputs.circle });
const alignOpt = new Inputs.OCCT.AlignDto();
alignOpt.fromDirection = cirDir;
alignOpt.toDirection = [0, 0, 1];
alignOpt.fromOrigin = cirPos;
alignOpt.toOrigin = [0, 0, 0];
alignOpt.shape = inputs.circle;
const circleAligned = this.transformsService.align(alignOpt);
const ptVertex1 = this.entitiesService.makeVertex(inputs.point1);
alignOpt.shape = ptVertex1;
const ptVertex1Aligned = this.transformsService.align(alignOpt);
ptVertex1.delete();
const pt1Aligned = this.converterService.vertexToPoint({ shape: ptVertex1Aligned });
ptVertex1Aligned.delete();
const pt2d1 = this.entitiesService.gpPnt2d([pt1Aligned[0], pt1Aligned[1]]);
const ptVertex2 = this.entitiesService.makeVertex(inputs.point2);
alignOpt.shape = ptVertex2;
const ptVertex2Aligned = this.transformsService.align(alignOpt);
ptVertex2.delete();
const pt2Aligned = this.converterService.vertexToPoint({ shape: ptVertex2Aligned });
ptVertex2Aligned.delete();
const pt2d2 = this.entitiesService.gpPnt2d([pt2Aligned[0], pt2Aligned[1]]);
const circle = this.getGpCircle2dFromEdge({ shape: circleAligned });
circleAligned.delete();
const qCircle = new this.occ.GccEnt_QualifiedCirc(circle, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
circle.delete();
const lin1 = this.occ.GccAna_Lin2d2Tan_fromQualifiedCircAndPoint(qCircle, pt2d1, inputs.tolerance);
const lin2 = this.occ.GccAna_Lin2d2Tan_fromQualifiedCircAndPoint(qCircle, pt2d2, inputs.tolerance);
qCircle.delete();
const solutions1 = [];
for (let i = 1; i <= lin1.NbSolutions(); i++) {
const sol = lin1.ThisSolution(i);
const location = sol.Location();
const edgeLine = this.lineEdge({ start: [location.X(), location.Y(), 0], end: pt1Aligned });
alignOpt.fromDirection = [0, 0, 1];
alignOpt.toDirection = cirDir;
alignOpt.fromOrigin = [0, 0, 0];
alignOpt.toOrigin = cirPos;
alignOpt.shape = edgeLine;
const aligned = this.transformsService.align(alignOpt);
solutions1.push(aligned);
sol.delete();
location.delete();
edgeLine.delete();
}
lin1.delete();
const solutions2 = [];
for (let i = 1; i <= lin2.NbSolutions(); i++) {
const sol = lin2.ThisSolution(i);
const location = sol.Location();
const edgeLine = this.lineEdge({ start: [location.X(), location.Y(), 0], end: pt2Aligned });
alignOpt.fromDirection = [0, 0, 1];
alignOpt.toDirection = cirDir;
alignOpt.fromOrigin = [0, 0, 0];
alignOpt.toOrigin = cirPos;
alignOpt.shape = edgeLine;
const aligned = this.transformsService.align(alignOpt);
solutions2.push(aligned);
sol.delete();
location.delete();
edgeLine.delete();
}
lin2.delete();
let resultingSol = [];
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
resultingSol = [...solutions1, ...solutions2];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
resultingSol = [solutions1[1], solutions2[0]];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2) {
resultingSol = [solutions1[0], solutions2[1]];
}
else {
resultingSol = [...solutions1, ...solutions2];
}
if (resultingSol.length === 2 && inputs.circleRemainder !== Inputs.OCCT.circleInclusionEnum.none) {
let startPoint;
let endPoint;
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2) {
if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide1) {
startPoint = this.startPointOnEdge({ shape: resultingSol[1] });
endPoint = this.startPointOnEdge({ shape: resultingSol[0] });
}
else if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide2) {
startPoint = this.startPointOnEdge({ shape: resultingSol[0] });
endPoint = this.startPointOnEdge({ shape: resultingSol[1] });
}
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide1) {
startPoint = this.startPointOnEdge({ shape: resultingSol[0] });
endPoint = this.startPointOnEdge({ shape: resultingSol[1] });
}
else if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide2) {
startPoint = this.startPointOnEdge({ shape: resultingSol[1] });
endPoint = this.startPointOnEdge({ shape: resultingSol[0] });
}
}
const edge = this.arcFromCircleAndTwoPoints({ circle: inputs.circle, start: startPoint, end: endPoint, sense: true });
resultingSol.splice(1, 0, edge);
}
return resultingSol;
}
constraintTanLinesFromPtToCircle(inputs) {
const cirDir = this.getCircularEdgePlaneDirection({ shape: inputs.circle });
const cirPos = this.getCircularEdgeCenterPoint({ shape: inputs.circle });
const alignOpt = new Inputs.OCCT.AlignDto();
alignOpt.fromDirection = cirDir;
alignOpt.toDirection = [0, 0, 1];
alignOpt.fromOrigin = cirPos;
alignOpt.toOrigin = [0, 0, 0];
alignOpt.shape = inputs.circle;
const circleAligned = this.transformsService.align(alignOpt);
const ptVertex = this.entitiesService.makeVertex(inputs.point);
alignOpt.shape = ptVertex;
const ptVertexAligned = this.transformsService.align(alignOpt);
ptVertex.delete();
const ptAligned = this.converterService.vertexToPoint({ shape: ptVertexAligned });
ptVertexAligned.delete();
const pt2d = this.entitiesService.gpPnt2d([ptAligned[0], ptAligned[1]]);
const circle = this.getGpCircle2dFromEdge({ shape: circleAligned });
circleAligned.delete();
const qCircle = new this.occ.GccEnt_QualifiedCirc(circle, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
circle.delete();
const lin = this.occ.GccAna_Lin2d2Tan_fromQualifiedCircAndPoint(qCircle, pt2d, inputs.tolerance);
qCircle.delete();
const solutions = [];
for (let i = 1; i <= lin.NbSolutions(); i++) {
const sol = lin.ThisSolution(i);
const location = sol.Location();
const edgeLine = this.lineEdge({ start: [location.X(), location.Y(), 0], end: ptAligned });
alignOpt.fromDirection = [0, 0, 1];
alignOpt.toDirection = cirDir;
alignOpt.fromOrigin = [0, 0, 0];
alignOpt.toOrigin = cirPos;
alignOpt.shape = edgeLine;
const aligned = this.transformsService.align(alignOpt);
solutions.push(aligned);
sol.delete();
location.delete();
edgeLine.delete();
}
lin.delete();
let resultingSol = [];
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
resultingSol = [...solutions];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
resultingSol = [solutions[0]];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2) {
resultingSol = [solutions[1]];
}
else {
resultingSol = [...solutions];
}
if (resultingSol.length === 2 && inputs.circleRemainder !== Inputs.OCCT.circleInclusionEnum.none) {
let startPoint;
let endPoint;
if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide1) {
startPoint = this.startPointOnEdge({ shape: resultingSol[1] });
endPoint = this.startPointOnEdge({ shape: resultingSol[0] });
}
else if (inputs.circleRemainder === Inputs.OCCT.circleInclusionEnum.keepSide2) {
startPoint = this.startPointOnEdge({ shape: resultingSol[0] });
endPoint = this.startPointOnEdge({ shape: resultingSol[1] });
}
const edge = this.arcFromCircleAndTwoPoints({ circle: inputs.circle, start: startPoint, end: endPoint, sense: true });
resultingSol.splice(1, 0, edge);
}
return resultingSol;
}
constraintTanLinesOnTwoCircles(inputs) {
const cirDir = this.getCircularEdgePlaneDirection({ shape: inputs.circle1 });
const cirPos = this.getCircularEdgeCenterPoint({ shape: inputs.circle1 });
const alignOpt = new Inputs.OCCT.AlignDto();
alignOpt.fromDirection = cirDir;
alignOpt.toDirection = [0, 0, 1];
alignOpt.fromOrigin = cirPos;
alignOpt.toOrigin = [0, 0, 0];
alignOpt.shape = inputs.circle1;
const circle1Aligned = this.transformsService.align(alignOpt);
alignOpt.shape = inputs.circle2;
const circle2Aligned = this.transformsService.align(alignOpt);
const circle1 = this.getGpCircle2dFromEdge({ shape: circle1Aligned });
const circle2 = this.getGpCircle2dFromEdge({ shape: circle2Aligned });
circle1Aligned.delete();
circle2Aligned.delete();
const qCircle1 = new this.occ.GccEnt_QualifiedCirc(circle1, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
const qCircle2 = new this.occ.GccEnt_QualifiedCirc(circle2, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
circle1.delete();
circle2.delete();
const lin1 = this.occ.GccAna_Lin2d2Tan_fromTwoQualifiedCirc(qCircle1, qCircle2, inputs.tolerance);
const lin2 = this.occ.GccAna_Lin2d2Tan_fromTwoQualifiedCirc(qCircle2, qCircle1, inputs.tolerance);
qCircle1.delete();
qCircle2.delete();
const lin1Sols = [];
for (let i = 1; i <= lin1.NbSolutions(); i++) {
const sol = lin1.ThisSolution(i);
lin1Sols.push(sol);
}
lin1.delete();
const lin2Sols = [];
for (let i = 1; i <= lin2.NbSolutions(); i++) {
const sol = lin2.ThisSolution(i);
lin2Sols.push(sol);
}
lin2.delete();
let adjustLin2Sol;
if (lin2Sols.length === 4) {
adjustLin2Sol = [lin2Sols[2], lin2Sols[1], lin2Sols[0], lin2Sols[3]];
}
else if (lin2Sols.length === 2) {
adjustLin2Sol = [lin2Sols[1], lin2Sols[0]];
}
const solutions = [];
for (let i = 0; i < lin1Sols.length; i++) {
const sol1 = lin1Sols[i];
const sol2 = adjustLin2Sol[i];
const locationStart = sol1.Location();
const startPoint = [locationStart.X(), locationStart.Y(), 0];
const locationEnd = sol2.Location();
const endPoint = [locationEnd.X(), locationEnd.Y(), 0];
const edgeLine = this.lineEdge({ start: startPoint, end: endPoint });
alignOpt.fromDirection = [0, 0, 1];
alignOpt.toDirection = cirDir;
alignOpt.fromOrigin = [0, 0, 0];
alignOpt.toOrigin = cirPos;
alignOpt.shape = edgeLine;
const aligned = this.transformsService.align(alignOpt);
solutions.push(aligned);
edgeLine.delete();
sol1.delete();
sol2.delete();
locationStart.delete();
locationEnd.delete();
}
let resultingSol = [];
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
resultingSol = [...solutions];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1 && solutions.length === 4) {
resultingSol = [solutions[1], solutions[3]];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 && solutions.length === 4) {
resultingSol = [solutions[0], solutions[2]];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1 && solutions.length === 2) {
resultingSol = [];
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 && solutions.length === 2) {
resultingSol = [solutions[0], solutions[1]];
}
else {
resultingSol = [...solutions];
}
if (resultingSol.length === 2 && inputs.circleRemainders !== Inputs.OCCT.twoCircleInclusionEnum.none) {
let startPoint1;
let startPoint2;
let endPoint1;
let endPoint2;
if (inputs.circleRemainders === Inputs.OCCT.twoCircleInclusionEnum.outside) {
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 || inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[1] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[0] });
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[0] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[1] });
}
endPoint1 = this.endPointOnEdge({ shape: resultingSol[0] });
endPoint2 = this.endPointOnEdge({ shape: resultingSol[1] });
}
else if (inputs.circleRemainders === Inputs.OCCT.twoCircleInclusionEnum.inside) {
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 || inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[0] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[1] });
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[1] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[0] });
}
endPoint1 = this.endPointOnEdge({ shape: resultingSol[1] });
endPoint2 = this.endPointOnEdge({ shape: resultingSol[0] });
}
else if (inputs.circleRemainders === Inputs.OCCT.twoCircleInclusionEnum.insideOutside) {
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 || inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[0] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[1] });
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[1] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[0] });
}
endPoint1 = this.endPointOnEdge({ shape: resultingSol[0] });
endPoint2 = this.endPointOnEdge({ shape: resultingSol[1] });
}
else if (inputs.circleRemainders === Inputs.OCCT.twoCircleInclusionEnum.outsideInside) {
if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide2 || inputs.positionResult === Inputs.OCCT.positionResultEnum.all) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[1] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[0] });
}
else if (inputs.positionResult === Inputs.OCCT.positionResultEnum.keepSide1) {
startPoint1 = this.startPointOnEdge({ shape: resultingSol[0] });
startPoint2 = this.startPointOnEdge({ shape: resultingSol[1] });
}
endPoint1 = this.endPointOnEdge({ shape: resultingSol[1] });
endPoint2 = this.endPointOnEdge({ shape: resultingSol[0] });
}
const edge1 = this.arcFromCircleAndTwoPoints({ circle: inputs.circle1, start: startPoint1, end: startPoint2, sense: true });
const edge2 = this.arcFromCircleAndTwoPoints({ circle: inputs.circle2, start: endPoint1, end: endPoint2, sense: true });
resultingSol.unshift(edge1);
resultingSol.push(edge2);
}
return resultingSol;
}
constraintTanCirclesOnCircleAndPnt(inputs) {
const cirDir = this.getCircularEdgePlaneDirection({ shape: inputs.circle });
const cirPos = this.getCircularEdgeCenterPoint({ shape: inputs.circle });
const { alignOpt, circle1Aligned: circleAligned } = this.alignCircle(cirDir, cirPos, inputs.circle);
const ptVertex = this.entitiesService.makeVertex(inputs.point);
alignOpt.shape = ptVertex;
const ptVertexAligned = this.transformsService.align(alignOpt);
ptVertex.delete();
const ptAligned = this.converterService.vertexToPoint({ shape: ptVertexAligned });
ptVertexAligned.delete();
const pt2d = this.entitiesService.gpPnt2d([ptAligned[0], ptAligned[1]]);
const circle = this.getGpCircle2dFromEdge({ shape: circleAligned });
circleAligned.delete();
const qCircle = new this.occ.GccEnt_QualifiedCirc(circle, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
circle.delete();
const lin1 = this.occ.GccAna_Circ2d2TanRad_fromQualifiedCircAndPoint(qCircle, pt2d, inputs.radius, inputs.tolerance);
qCircle.delete();
const lin1Sols = [];
for (let i = 1; i <= lin1.NbSolutions(); i++) {
const sol = lin1.ThisSolution(i);
lin1Sols.push(sol);
}
lin1.delete();
const solutions = [];
for (let i = 0; i < lin1Sols.length; i++) {
const sol = lin1Sols[i];
const res = this.reconstructCircleAndAlignBack(lin1Sols, sol, alignOpt, cirDir, cirPos);
solutions.push(res);
}
return solutions;
}
alignCircle(cirDir, cirPos, circle) {
const alignOpt = new Inputs.OCCT.AlignDto();
alignOpt.fromDirection = cirDir;
alignOpt.toDirection = [0, 0, 1];
alignOpt.fromOrigin = cirPos;
alignOpt.toOrigin = [0, 0, 0];
alignOpt.shape = circle;
const circle1Aligned = this.transformsService.align(alignOpt);
return { alignOpt, circle1Aligned };
}
constraintTanCirclesOnTwoCircles(inputs) {
const cirDir = this.getCircularEdgePlaneDirection({ shape: inputs.circle1 });
const cirPos = this.getCircularEdgeCenterPoint({ shape: inputs.circle1 });
const { alignOpt, circle1Aligned } = this.alignCircle(cirDir, cirPos, inputs.circle1);
alignOpt.shape = inputs.circle2;
const circle2Aligned = this.transformsService.align(alignOpt);
const circle1 = this.getGpCircle2dFromEdge({ shape: circle1Aligned });
const circle2 = this.getGpCircle2dFromEdge({ shape: circle2Aligned });
circle1Aligned.delete();
circle2Aligned.delete();
const qCircle1 = new this.occ.GccEnt_QualifiedCirc(circle1, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
const qCircle2 = new this.occ.GccEnt_QualifiedCirc(circle2, this.enumService.getGccEntPositionFromEnum(Inputs.OCCT.gccEntPositionEnum.unqualified));
circle1.delete();
circle2.delete();
const lin1 = this.occ.GccAna_Circ2d2TanRad_fromTwoQualifiedCirc(qCircle1, qCircle2, inputs.radius, inputs.tolerance);
qCircle1.delete();
qCircle2.delete();
const lin1Sols = [];
for (let i = 1; i <= lin1.NbSolutions(); i++) {
const sol = lin1.ThisSolution(i);
lin1Sols.push(sol);
}
lin1.delete();
const solutions = [];
for (let i = 0; i < lin1Sols.length; i++) {
const sol = lin1Sols[i];
const res = this.reconstructCircleAndAlignBack(lin1Sols, sol, alignOpt, cirDir, cirPos);
solutions.push(res);
}
return solutions;
}
divideEdgeByParamsToPoints(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const points = this.geomService.divideCurveToNrSegments(Object.assign(Object.assign({}, inputs), { shape: curve }), curve.FirstParameter(), curve.LastParameter());
curve.delete();
wire.delete();
return points;
}
divideEdgeByEqualDistanceToPoints(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const points = this.geomService.divideCompCurveByEqualLengthDistance(Object.assign(Object.assign({}, inputs), { shape: curve }));
curve.delete();
wire.delete();
return points;
}
pointOnEdgeAtParam(inputs) {
const edge = inputs.shape;
const { uMin, uMax } = this.getEdgeBounds(edge);
const param = this.vecHelper.remap(inputs.param, 0, 1, uMin, uMax);
const result = this.occ.EvaluateEdgeCurve(edge, param);
if (result && result.IsValid) {
const pt = [result.Point.X(), result.Point.Y(), result.Point.Z()];
result.delete();
return pt;
}
else {
return undefined;
}
}
reconstructCircleAndAlignBack(lin1Sols, sol, alignOpt, dir, pos) {
const locationStart = sol.Location();
const startPoint = [locationStart.X(), locationStart.Y(), 0];
const circle = this.entitiesService.createCircle(sol.Radius(), startPoint, [0, 0, 1], Inputs.OCCT.typeSpecificityEnum.edge);
alignOpt.fromDirection = [0, 0, 1];
alignOpt.toDirection = dir;
alignOpt.fromOrigin = [0, 0, 0];
alignOpt.toOrigin = pos;
alignOpt.shape = circle;
const aligned = this.transformsService.align(alignOpt);
circle.delete();
sol.delete();
locationStart.delete();
return aligned;
}
arcFromCircleAndTwoPoints(inputs) {
const circle = this.getGpCircleFromEdge({ shape: inputs.circle });
const gpPnt1 = this.entitiesService.gpPnt(inputs.start);
const gpPnt2 = this.entitiesService.gpPnt(inputs.end);
const shape = this.occ.MakeArcOnCircle(circle, gpPnt1, gpPnt2, inputs.sense);
circle.delete();
gpPnt1.delete();
gpPnt2.delete();
return shape;
}
getCircularEdgePlaneDirection(inputs) {
const circle = this.getGpCircleFromEdge(inputs);
const axis = circle.Position();
const dir = axis.Direction();
const result = [dir.X(), dir.Y(), dir.Z()];
dir.delete();
axis.delete();
circle.delete();
return result;
}
getCircularEdgeCenterPoint(inputs) {
const circle = this.getGpCircleFromEdge(inputs);
const location = circle.Location();
const result = [location.X(), location.Y(), location.Z()];
location.delete();
circle.delete();
return result;
}
getCircularEdgeRadius(inputs) {
const circle = this.getGpCircleFromEdge(inputs);
const radius = circle.Radius();
circle.delete();
return radius;
}
getGpCircleFromEdge(inputs) {
const curve = new this.occ.BRepAdaptor_Curve(inputs.shape);
try {
const circle = curve.Circle();
curve.delete();
return circle;
}
catch (ex) {
curve.delete();
throw new Error("Edge is not a circular edge.");
}
}
getGpCircle2dFromEdge(inputs) {
const curve = new this.occ.BRepAdaptor_Curve(inputs.shape);
try {
const circle = curve.Circle();
const ax = circle.Position();
const location = circle.Location();
const ax2d = this.entitiesService.gpAx2d([location.X(), location.Y()], [1, 0]);
const radius = circle.Radius();
const circle2d = new this.occ.gp_Circ2d(ax2d, radius);
curve.delete();
circle.delete();
ax.delete();
location.delete();
ax2d.delete();
return circle2d;
}
catch (ex) {
curve.delete();
throw new Error("Edge is not a circular edge.");
}
}
tangentOnEdgeAtParam(inputs) {
const edge = inputs.shape;
// C++ function expects normalized [0,1] parameter and does its own remapping
const result = this.occ.GetDerivativesOnEdgeAtParam(edge, inputs.param);
if (result && result.isValid) {
return [result.d1x, result.d1y, result.d1z];
}
throw new Error("Could not get tangent on edge at param");
}
pointOnEdgeAtLength(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const res = this.geomService.pointOnCompCurveAtLength(Object.assign(Object.assign({}, inputs), { shape: curve }));
curve.delete();
wire.delete();
return res;
}
tangentOnEdgeAtLength(inputs) {
const edge = inputs.shape;
const wire = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge] });
const curve = new this.occ.BRepAdaptor_CompCurve(wire, false);
const res = this.geomService.tangentOnCurveAtLengthCompCurve(Object.assign(Object.assign({}, inputs), { shape: curve }));
wire.delete();
curve.delete();
return res;
}
getEdgeBounds(edge) {
const result = this.occ.BRep_Tool_GetEdgeParameters(edge);
if (result.IsValid) {
return { uMin: result.First, uMax: result.Last };
}
return { uMin: 0, uMax: 0 };
}
isEdgeCircular(inputs) {
return this.occ.IsEdgeCircular(inputs.shape);
}
isEdgeLinear(inputs) {
return this.occ.IsEdgeLinear(inputs.shape);
}
/**
* Create symmetric periodic (closed) BSpline edge through points
* Uses chord-based tangent constraints to ensure the curve is symmetrical
* (e.g., 4 points of a square will produce a perfectly symmetric curve like Rhino)
* @param inputs Points to interpolate
* @returns Symmetric periodic BSpline edge
*/
createSymmetricPeriodicBSplineEdge(inputs) {
// Create flat array of coordinates for the new API
const coords = new this.occ.VectorDouble();
for (const pt of inputs.points) {
coords.push_back(pt[0]);
coords.push_back(pt[1]);
coords.push_back(pt[2]);
}
const edge = this.occ.MakeSymmetricPeriodicBSplineEdge(coords);
coords.delete();
if (edge && !edge.IsNull()) {
return edge;
}
else {
return undefined;
}
}
}