UNPKG

@bitbybit-dev/occt

Version:

Bit By Bit Developers CAD algorithms using OpenCascade Technology kernel. Run in Node and in Browser.

660 lines (659 loc) 28.7 kB
import * as Inputs from "../../api/inputs/inputs"; export class OperationsService { constructor(occ, enumService, entitiesService, converterService, booleansService, shapeGettersService, edgesService, transformsService, vecHelper, wiresService, facesService, solidsService, shellsService) { this.occ = occ; this.enumService = enumService; this.entitiesService = entitiesService; this.converterService = converterService; this.booleansService = booleansService; this.shapeGettersService = shapeGettersService; this.edgesService = edgesService; this.transformsService = transformsService; this.vecHelper = vecHelper; this.wiresService = wiresService; this.facesService = facesService; this.solidsService = solidsService; this.shellsService = shellsService; } loftAdvanced(inputs) { if (inputs.periodic && !inputs.closed) { throw new Error("Cant construct periodic non closed loft."); } const pipe = new this.occ.BRepOffsetAPI_ThruSections(inputs.makeSolid, inputs.straight, inputs.tolerance); const wires = []; const vertices = []; if (inputs.startVertex) { const v = this.entitiesService.makeVertex(inputs.startVertex); pipe.AddVertex(v); vertices.push(v); } if (inputs.closed && !inputs.periodic) { inputs.shapes.push(inputs.shapes[0]); } else if (inputs.closed && inputs.periodic) { const pointsOnCrvs = []; inputs.shapes.forEach((s) => { if (this.enumService.getShapeTypeEnum(s) === Inputs.OCCT.shapeTypeEnum.edge) { s = this.entitiesService.bRepBuilderAPIMakeWire(s); } const pts = this.wiresService.divideWireByParamsToPoints({ shape: s, nrOfDivisions: inputs.nrPeriodicSections, removeStartPoint: false, removeEndPoint: false }); pointsOnCrvs.push(pts); }); // <= needed due to start and end points that are added for (let i = 0; i <= inputs.nrPeriodicSections; i++) { const ptsForPerpWire = pointsOnCrvs.map(p => p[i]); const periodicWire = this.wiresService.interpolatePoints({ points: ptsForPerpWire, tolerance: inputs.tolerance, periodic: true }); pipe.AddWire(periodicWire); wires.push(periodicWire); } } if (!inputs.periodic) { inputs.shapes.forEach((wire) => { pipe.AddWire(wire); }); } const endVertices = []; if (inputs.endVertex) { const v = this.entitiesService.makeVertex(inputs.endVertex); pipe.AddVertex(v); endVertices.push(v); } if (inputs.useSmoothing) { pipe.SetSmoothing(inputs.useSmoothing); } if (inputs.maxUDegree) { pipe.SetMaxDegree(inputs.maxUDegree); } let parType = undefined; if (inputs.parType === Inputs.OCCT.approxParametrizationTypeEnum.approxChordLength) { parType = this.occ.Approx_ParametrizationType.Approx_ChordLength; } else if (inputs.parType === Inputs.OCCT.approxParametrizationTypeEnum.approxCentripetal) { parType = this.occ.Approx_ParametrizationType.Approx_Centripetal; } else if (inputs.parType === Inputs.OCCT.approxParametrizationTypeEnum.approxIsoParametric) { parType = this.occ.Approx_ParametrizationType.Approx_IsoParametric; } if (parType) { pipe.SetParType(parType); } pipe.CheckCompatibility(false); const pipeShape = pipe.Shape(); const res = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); wires.forEach(w => w.delete()); vertices.forEach(v => v.delete()); endVertices.forEach(v => v.delete()); return res; } closestPointsBetweenTwoShapes(shape1, shape2) { const messageProgress = new this.occ.Message_ProgressRange_1(); const extrema = new this.occ.BRepExtrema_DistShapeShape_2(shape1, shape2, this.occ.Extrema_ExtFlag.Extrema_ExtFlag_MIN, this.occ.Extrema_ExtAlgo.Extrema_ExtAlgo_Grad, messageProgress); const messageProgress1 = new this.occ.Message_ProgressRange_1(); extrema.Perform(messageProgress1); if (extrema.IsDone() && extrema.NbSolution() > 0) { const closestPoint1 = extrema.PointOnShape1(1); const closestPoint2 = extrema.PointOnShape2(1); return [[closestPoint1.X(), closestPoint1.Y(), closestPoint1.Z()], [closestPoint2.X(), closestPoint2.Y(), closestPoint2.Z()]]; } else { throw new Error("Closest points could not be found."); } } closestPointsOnShapeFromPoints(inputs) { const vertexes = inputs.points.map(p => this.entitiesService.makeVertex(p)); const pointsOnShape = vertexes.map(v => this.closestPointsBetweenTwoShapes(v, inputs.shape)); return pointsOnShape.map(p => p[1]); } closestPointsOnShapesFromPoints(inputs) { const vertexes = inputs.points.map(p => this.entitiesService.makeVertex(p)); const result = []; inputs.shapes.forEach((s) => { const pointsOnShape = vertexes.map(v => this.closestPointsBetweenTwoShapes(v, s)); result.push(...pointsOnShape.map(p => p[1])); }); return result; } distancesToShapeFromPoints(inputs) { const vertexes = inputs.points.map(p => this.entitiesService.makeVertex(p)); const pointsOnShape = vertexes.map(v => this.closestPointsBetweenTwoShapes(v, inputs.shape)); return pointsOnShape.map(p => { return this.vecHelper.distanceBetweenPoints(p[0], p[1]); }); } boundingBoxOfShape(inputs) { const bbox = new this.occ.Bnd_Box_1(); this.occ.BRepBndLib.Add(inputs.shape, bbox, false); const cornerMin = bbox.CornerMin(); const cornerMax = bbox.CornerMax(); const min = [cornerMin.X(), cornerMin.Y(), cornerMin.Z()]; const max = [cornerMax.X(), cornerMax.Y(), cornerMax.Z()]; const center = [ (cornerMin.X() + cornerMax.X()) / 2, (cornerMin.Y() + cornerMax.Y()) / 2, (cornerMin.Z() + cornerMax.Z()) / 2 ]; const size = [ cornerMax.X() - cornerMin.X(), cornerMax.Y() - cornerMin.Y(), cornerMax.Z() - cornerMin.Z() ]; bbox.delete(); const result = { min, max, center, size }; return result; } boundingBoxShapeOfShape(inputs) { const bbox = this.boundingBoxOfShape(inputs); return this.solidsService.createBoxFromCorner({ corner: bbox.min, width: bbox.size[0], height: bbox.size[1], length: bbox.size[2], }); } boundingSphereOfShape(inputs) { const bbox = this.boundingBoxOfShape(inputs); const center = bbox.center; const radius = this.vecHelper.distanceBetweenPoints(bbox.min, center); const result = { center, radius }; return result; } boundingSphereShapeOfShape(inputs) { const bbox = this.boundingSphereOfShape(inputs); return this.solidsService.createSphere({ center: bbox.center, radius: bbox.radius, }); } loft(inputs) { const pipe = new this.occ.BRepOffsetAPI_ThruSections(inputs.makeSolid, false, 1.0e-06); inputs.shapes.forEach((wire) => { if (this.enumService.getShapeTypeEnum(wire) === Inputs.OCCT.shapeTypeEnum.edge) { wire = this.entitiesService.bRepBuilderAPIMakeWire(wire); } pipe.AddWire(wire); }); pipe.CheckCompatibility(false); const pipeShape = pipe.Shape(); const res = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); return res; } offset(inputs) { return this.offsetAdv({ shape: inputs.shape, face: inputs.face, distance: inputs.distance, tolerance: inputs.tolerance, joinType: Inputs.OCCT.joinTypeEnum.arc, removeIntEdges: false }); } offsetAdv(inputs) { if (!inputs.tolerance) { inputs.tolerance = 0.1; } if (inputs.distance === 0.0) { return inputs.shape; } let offset; const joinType = this.getJoinType(inputs.joinType); // only this mode is implemented currently, so we cannot expose others... const brepOffsetMode = this.occ.BRepOffset_Mode.BRepOffset_Skin; const wires = []; if ((this.enumService.getShapeTypeEnum(inputs.shape) === Inputs.OCCT.shapeTypeEnum.wire || this.enumService.getShapeTypeEnum(inputs.shape) === Inputs.OCCT.shapeTypeEnum.edge)) { let wire; if (this.enumService.getShapeTypeEnum(inputs.shape) === Inputs.OCCT.shapeTypeEnum.edge) { wire = this.entitiesService.bRepBuilderAPIMakeWire(inputs.shape); wires.push(wire); } else { wire = inputs.shape; } try { offset = new this.occ.BRepOffsetAPI_MakeOffset_1(); if (inputs.face) { offset.Init_1(inputs.face, joinType, false); } else { offset.Init_2(joinType, false); } offset.AddWire(wire); const messageProgress1 = new this.occ.Message_ProgressRange_1(); offset.Build(messageProgress1); offset.Perform(inputs.distance, 0.0); } catch (ex) { offset = new this.occ.BRepOffsetAPI_MakeOffsetShape(); offset.PerformByJoin(wire, inputs.distance, inputs.tolerance, brepOffsetMode, false, false, joinType, inputs.removeIntEdges, new this.occ.Message_ProgressRange_1()); } } else { const shapeToOffset = inputs.shape; offset = new this.occ.BRepOffsetAPI_MakeOffsetShape(); offset.PerformByJoin(shapeToOffset, inputs.distance, inputs.tolerance, brepOffsetMode, false, false, joinType, inputs.removeIntEdges, new this.occ.Message_ProgressRange_1()); } const offsetShape = offset.Shape(); const result = this.converterService.getActualTypeOfShape(offsetShape); offsetShape.delete(); if (offset) { offset.delete(); } wires.forEach(w => w.delete()); return result; } offset3DWire(inputs) { const extrusion = this.extrude({ shape: inputs.shape, direction: inputs.direction, }); const thickSolid = this.makeThickSolidSimple({ shape: extrusion, offset: inputs.offset, }); const nrOfEdges = this.shapeGettersService.getEdges({ shape: inputs.shape }).length; const predictedNrOfFaces = nrOfEdges * 4 + 2; const lastFaceIndex = predictedNrOfFaces / 2 - 1; const firstFaceIndex = lastFaceIndex - nrOfEdges + 1; const faceEdges = []; this.shapeGettersService.getFaces({ shape: thickSolid }).forEach((f, index) => { if (index >= firstFaceIndex && index <= lastFaceIndex) { const firstEdge = this.shapeGettersService.getEdges({ shape: f })[2]; faceEdges.push(firstEdge); } }); let result; try { result = this.converterService.combineEdgesAndWiresIntoAWire({ shapes: faceEdges }); result = result.Reversed(); result = this.converterService.getActualTypeOfShape(result); } catch (_a) { result = faceEdges; return result; } return result; } extrudeShapes(inputs) { return inputs.shapes.map(shape => { const extruded = this.extrude({ shape, direction: inputs.direction }); const result = this.converterService.getActualTypeOfShape(extruded); extruded.delete(); return result; }); } extrude(inputs) { const gpVec = new this.occ.gp_Vec_4(inputs.direction[0], inputs.direction[1], inputs.direction[2]); const prismMaker = new this.occ.BRepPrimAPI_MakePrism_1(inputs.shape, gpVec, false, true); const prismShape = prismMaker.Shape(); prismMaker.delete(); gpVec.delete(); return prismShape; } splitShapeWithShapes(inputs) { const bopalgoBuilder = new this.occ.BOPAlgo_Builder_1(); bopalgoBuilder.SetNonDestructive(inputs.nonDestructive); bopalgoBuilder.SetFuzzyValue(inputs.localFuzzyTolerance); bopalgoBuilder.AddArgument(inputs.shape); inputs.shapes.forEach(s => { bopalgoBuilder.AddArgument(s); }); bopalgoBuilder.Perform(new this.occ.Message_ProgressRange_1()); let shapes; if (!inputs.nonDestructive) { const res = bopalgoBuilder.Modified(inputs.shape); const shapeCompound = this.occ.BitByBitDev.BitListOfShapesToCompound(res); shapes = this.shapeGettersService.getShapesOfCompound({ shape: shapeCompound }); } else { const res = bopalgoBuilder.Shape(); shapes = this.shapeGettersService.getShapesOfCompound({ shape: res }); } return shapes; } revolve(inputs) { if (!inputs.angle) { inputs.angle = 360.0; } if (!inputs.direction) { inputs.direction = [0, 0, 1]; } let result; if (inputs.angle >= 360.0) { const pt1 = new this.occ.gp_Pnt_3(0, 0, 0); const dir = new this.occ.gp_Dir_4(inputs.direction[0], inputs.direction[1], inputs.direction[2]); const ax1 = new this.occ.gp_Ax1_2(pt1, dir); const makeRevol = new this.occ.BRepPrimAPI_MakeRevol_2(inputs.shape, ax1, inputs.copy); result = makeRevol.Shape(); makeRevol.delete(); pt1.delete(); dir.delete(); ax1.delete(); } else { const pt1 = new this.occ.gp_Pnt_3(0, 0, 0); const dir = new this.occ.gp_Dir_4(inputs.direction[0], inputs.direction[1], inputs.direction[2]); const ax1 = new this.occ.gp_Ax1_2(pt1, dir); const makeRevol = new this.occ.BRepPrimAPI_MakeRevol_1(inputs.shape, ax1, inputs.angle * 0.0174533, inputs.copy); result = makeRevol.Shape(); makeRevol.delete(); pt1.delete(); dir.delete(); ax1.delete(); } const actual = this.converterService.getActualTypeOfShape(result); result.delete(); return actual; } rotatedExtrude(inputs) { // Get the bounding box of the input shape to determine its current position const bbox = this.boundingBoxOfShape({ shape: inputs.shape }); const shapeStartY = bbox.min[1]; // Y coordinate of the bottom of the shape const shapeEndY = shapeStartY + inputs.height; // Target Y coordinate after extrusion const translatedShape = this.transformsService.translate({ translation: [0, inputs.height, 0], shape: inputs.shape, }); const upperPolygon = this.transformsService.rotate({ axis: [0, 1, 0], angle: inputs.angle, shape: translatedShape }); // Define the straight spine going from the shape's current position const spineWire = this.wiresService.createBSpline({ points: [ [0, shapeStartY, 0], [0, shapeEndY, 0] ], closed: false, }); // Define the guiding helical auxiliary spine (which controls the rotation) const steps = 30; const aspinePoints = []; for (let i = 0; i <= steps; i++) { const alpha = i / steps; aspinePoints.push([ 20 * Math.sin(alpha * inputs.angle * 0.0174533), shapeStartY + (inputs.height * alpha), 20 * Math.cos(alpha * inputs.angle * 0.0174533), ]); } const aspineWire = this.wiresService.createBSpline({ points: aspinePoints, closed: false }); // Sweep the face wires along the spine to create the extrusion const pipe = new this.occ.BRepOffsetAPI_MakePipeShell(spineWire); pipe.SetMode_5(aspineWire, true, this.occ.BRepFill_TypeOfContact.BRepFill_NoContact); pipe.Add_1(inputs.shape, false, false); pipe.Add_1(upperPolygon, false, false); pipe.Build(new this.occ.Message_ProgressRange_1()); // default should be to make the solid for backwards compatibility if (inputs.makeSolid || inputs.makeSolid === undefined) { pipe.MakeSolid(); } const pipeShape = pipe.Shape(); const result = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); aspineWire.delete(); spineWire.delete(); upperPolygon.delete(); translatedShape.delete(); return result; } pipe(inputs) { const pipe = new this.occ.BRepOffsetAPI_MakePipeShell(inputs.shape); inputs.shapes.forEach(sh => { pipe.Add_1(sh, false, false); }); pipe.Build(new this.occ.Message_ProgressRange_1()); pipe.MakeSolid(); const pipeShape = pipe.Shape(); const result = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); return result; } pipePolylineWireNGon(inputs) { // Input wire (the spine) const wire = inputs.shape; // Get the edges of the wire const edge = this.shapeGettersService.getEdge({ shape: wire, index: 1 }); // Get the start point and tangent of the first edge const startPoint = this.edgesService.pointOnEdgeAtParam({ shape: edge, param: 0 }); const tangent = this.edgesService.tangentOnEdgeAtParam({ shape: edge, param: 0 }); const ngon = this.wiresService.createNGonWire({ radius: inputs.radius, center: startPoint, direction: tangent, nrCorners: inputs.nrCorners }); const reversedNgon = this.wiresService.reversedWire({ shape: ngon }); let shape = reversedNgon; if (inputs.makeSolid) { shape = this.facesService.createFaceFromWire({ shape: reversedNgon, planar: true, }); } const geomFillTrihedron = this.enumService.getGeomFillTrihedronEnumOCCTValue(inputs.trihedronEnum); const pipe = new this.occ.BRepOffsetAPI_MakePipe_2(wire, shape, geomFillTrihedron, inputs.forceApproxC1 ? true : false); pipe.Build(new this.occ.Message_ProgressRange_1()); const pipeShape = pipe.Shape(); // Convert and clean up const result = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); ngon.delete(); reversedNgon.delete(); return result; } pipeWireCylindrical(inputs) { // Input wire (the spine) const wire = inputs.shape; // Get the edges of the wire const edges = this.shapeGettersService.getEdges({ shape: wire }); // Get the start point and tangent of the first edge const firstEdge = edges[0]; const startPoint = this.edgesService.startPointOnEdge({ shape: firstEdge }); const tangent = this.edgesService.tangentOnEdgeAtParam({ shape: firstEdge, param: 0 }); const circle = this.entitiesService.createCircle(inputs.radius, startPoint, tangent, inputs.makeSolid ? Inputs.OCCT.typeSpecificityEnum.face : Inputs.OCCT.typeSpecificityEnum.wire); // Create the pipe by sweeping the profile along the wire const geomFillTrihedron = this.enumService.getGeomFillTrihedronEnumOCCTValue(inputs.trihedronEnum); const pipe = new this.occ.BRepOffsetAPI_MakePipe_2(wire, circle, geomFillTrihedron, inputs.forceApproxC1 ? true : false); pipe.Build(new this.occ.Message_ProgressRange_1()); const pipeShape = pipe.Shape(); // Convert and clean up const result = this.converterService.getActualTypeOfShape(pipeShape); pipeShape.delete(); pipe.delete(); circle.delete(); return result; } pipeWiresCylindrical(inputs) { return inputs.shapes.map(wire => { return this.pipeWireCylindrical({ shape: wire, radius: inputs.radius, makeSolid: inputs.makeSolid, trihedronEnum: inputs.trihedronEnum, forceApproxC1: inputs.forceApproxC1 }); }); } makeThickSolidSimple(inputs) { const maker = new this.occ.BRepOffsetAPI_MakeThickSolid(); maker.MakeThickSolidBySimple(inputs.shape, inputs.offset); maker.Build(new this.occ.Message_ProgressRange_1()); const makerShape = maker.Shape(); const result = this.converterService.getActualTypeOfShape(makerShape); let res2 = result; if (inputs.offset > 0) { const faces = this.shapeGettersService.getFaces({ shape: result }); const revFaces = faces.map(face => face.Reversed()); res2 = this.shellsService.sewFaces({ shapes: revFaces, tolerance: 1e-7 }); result.delete(); } maker.delete(); makerShape.delete(); return res2; } makeThickSolidByJoin(inputs) { const facesToRemove = new this.occ.TopTools_ListOfShape_1(); inputs.shapes.forEach(shape => { facesToRemove.Append_1(shape); }); const myBody = new this.occ.BRepOffsetAPI_MakeThickSolid(); const jointType = this.getJoinType(inputs.joinType); myBody.MakeThickSolidByJoin(inputs.shape, facesToRemove, inputs.offset, inputs.tolerance, this.occ.BRepOffset_Mode.BRepOffset_Skin, // currently a single option inputs.intersection, inputs.selfIntersection, jointType, inputs.removeIntEdges, new this.occ.Message_ProgressRange_1()); const makeThick = myBody.Shape(); const result = this.converterService.getActualTypeOfShape(makeThick); makeThick.delete(); myBody.delete(); facesToRemove.delete(); return result; } getJoinType(jointType) { let res; switch (jointType) { case Inputs.OCCT.joinTypeEnum.arc: { res = this.occ.GeomAbs_JoinType.GeomAbs_Arc; break; } case Inputs.OCCT.joinTypeEnum.intersection: { res = this.occ.GeomAbs_JoinType.GeomAbs_Intersection; break; } case Inputs.OCCT.joinTypeEnum.tangent: { res = this.occ.GeomAbs_JoinType.GeomAbs_Tangent; break; } } return res; } getBRepOffsetMode(offsetMode) { let res; switch (offsetMode) { case Inputs.OCCT.bRepOffsetModeEnum.skin: { res = this.occ.BRepOffset_Mode.BRepOffset_Skin; break; } case Inputs.OCCT.bRepOffsetModeEnum.pipe: { res = this.occ.BRepOffset_Mode.BRepOffset_Pipe; break; } case Inputs.OCCT.bRepOffsetModeEnum.rectoVerso: { res = this.occ.BRepOffset_Mode.BRepOffset_RectoVerso; break; } } return res; } slice(inputs) { if (inputs.step <= 0) { throw new Error("Step needs to be positive."); } const { bbox, transformedShape } = this.createBBoxAndTransformShape(inputs.shape, inputs.direction); const intersections = []; if (!bbox.IsThin(0.0001)) { const { minY, maxY, maxDist } = this.computeBounds(bbox); const planes = []; for (let i = minY; i < maxY; i += inputs.step) { const pq = this.facesService.createSquareFace({ size: maxDist, center: [0, i, 0], direction: [0, 1, 0] }); planes.push(pq); } this.applySlices(transformedShape, planes, inputs.direction, intersections); } const res = this.converterService.makeCompound({ shapes: intersections }); return res; } sliceInStepPattern(inputs) { if (!inputs.steps || inputs.steps.length === 0) { throw new Error("Steps must be provided with at elast one positive value"); } const { bbox, transformedShape } = this.createBBoxAndTransformShape(inputs.shape, inputs.direction); const intersections = []; if (!bbox.IsThin(0.0001)) { const { minY, maxY, maxDist } = this.computeBounds(bbox); const planes = []; let index = 0; for (let i = minY; i < maxY; i += inputs.steps[index]) { const pq = this.facesService.createSquareFace({ size: maxDist, center: [0, i, 0], direction: [0, 1, 0] }); planes.push(pq); if (inputs.steps[index + 1] === undefined) { index = 0; } else { index++; } } this.applySlices(transformedShape, planes, inputs.direction, intersections); } const res = this.converterService.makeCompound({ shapes: intersections }); return res; } createBBoxAndTransformShape(shape, direction) { // we orient the given shape to the reverse direction of sections so that slicing // would always happen in flat bbox aligned orientation // after algorithm computes, we turn all intersections to original shape so that it would match a given shape. // const fromDir const transformedShape = this.transformsService.align({ shape, fromOrigin: [0, 0, 0], fromDirection: direction, toOrigin: [0, 0, 0], toDirection: [0, 1, 0], }); const bbox = new this.occ.Bnd_Box_1(); this.occ.BRepBndLib.Add(transformedShape, bbox, false); return { bbox, transformedShape }; } computeBounds(bbox) { const cornerMin = bbox.CornerMin(); const cornerMax = bbox.CornerMax(); const minY = cornerMin.Y(); const maxY = cornerMax.Y(); const minX = cornerMin.X(); const maxX = cornerMax.X(); const minZ = cornerMin.Z(); const maxZ = cornerMax.Z(); const distX = maxX - minX; const distZ = maxZ - minZ; const percentage = 1.2; let maxDist = distX >= distZ ? distX : distZ; maxDist *= percentage; return { minY, maxY, maxDist }; } applySlices(transformedShape, planes, direction, intersections) { const shapesToSlice = []; if (this.enumService.getShapeTypeEnum(transformedShape) === Inputs.OCCT.shapeTypeEnum.solid) { shapesToSlice.push(transformedShape); } else { const solids = this.shapeGettersService.getSolids({ shape: transformedShape }); shapesToSlice.push(...solids); } if (shapesToSlice.length === 0) { throw new Error("No solids found to slice."); } shapesToSlice.forEach(s => { const intInputs = new Inputs.OCCT.IntersectionDto(); intInputs.keepEdges = true; intInputs.shapes = [s]; const compound = this.converterService.makeCompound({ shapes: planes }); intInputs.shapes.push(compound); const ints = this.booleansService.intersection(intInputs); ints.forEach(int => { if (int && !int.IsNull()) { const transformedInt = this.transformsService.align({ shape: int, fromOrigin: [0, 0, 0], fromDirection: [0, 1, 0], toOrigin: [0, 0, 0], toDirection: direction, }); intersections.push(transformedInt); } }); }); } }