UNPKG

@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
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; } } }