TypeScript Code for Parametric Tower Facade

TypeScript Code for Parametric Tower Facade script details
Type
Typescript logo image
typescript
App Version
0.15.1
Visibility
public
Date Created
Jan 3, 2023, 2:04:42 PM
Last Edit Date
Mar 5, 2024, 10:14:15 AM

Script Details

The Code
type Point3 = Bit.Inputs.Base.Point3; type Vector3 = Bit.Inputs.Base.Point3; type ShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer; const approxParametrizationTypeEnum = Bit.Inputs.OCCT.approxParametrizationTypeEnum; const LoftAdvancedDto = Bit.Inputs.OCCT.LoftAdvancedDto; const SkyboxDto = Bit.Inputs.BabylonScene.SkyboxDto; const DirectionalLightDto = Bit.Inputs.BabylonScene.DirectionalLightDto; const DrawOcctShapeOptions = Bit.Inputs.Draw.DrawOcctShapeOptions; const PBRMetallicRoughnessDto = Bit.Inputs.BabylonMaterial.PBRMetallicRoughnessDto; const skyboxEnum = Bit.Inputs.Base.skyboxEnum; type UV = [ number, number, ] type CellUV = { ptUV1: UV, ptUV2: UV, ptUV3: UV, ptUV4: UV, center: UV, uIndex: number, vIndex: number, } type CellPt = { pt1: Point3, pt2: Point3, pt3: Point3, pt4: Point3, center: Point3, pt1Normal: Vector3, pt2Normal: Vector3, pt3Normal: Vector3, pt4Normal: Vector3, centerNormal: Vector3, uIndex: number, vIndex: number, } const exportGlb = false; const meshingPrecision = 0.01; const chunksize = 30; const drawEdges = true; const start = async () => { const asset = await bitbybit.asset.getFile({ fileName: 'tower-base.stp' }); const shape = await bitbybit.occt.io.loadSTEPorIGES({ assetFile: asset as File, adjustZtoY: true }); const scaledShape = await bitbybit.occt.transforms.scale({ shape, factor: 0.1 }); const face1 = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 4 }); const face1Of = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 2 }); const face2 = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 1 }); const face2Of = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 3 }); let faceTop = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 0 }); let faceSide1 = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 5 }); let faceSide2 = await bitbybit.occt.shapes.face.getFace({ shape: scaledShape, index: 6 }); faceSide1 = await bitbybit.occt.shapes.face.reversedFace({ shape: faceSide1 }); faceSide2 = await bitbybit.occt.shapes.face.reversedFace({ shape: faceSide2 }); if (!exportGlb) { const opt = new SkyboxDto(); opt.blur = 0.4; opt.skybox = skyboxEnum.clearSky; opt.size = 1000; opt.environmentIntensity = 0.7; bitbybit.babylon.scene.enableSkybox(opt); const lightOpt = new DirectionalLightDto(); lightOpt.direction = [-50, -10, -30]; lightOpt.intensity = 1; lightOpt.shadowGeneratorMapSize = 4000; bitbybit.babylon.scene.drawDirectionalLight(lightOpt) } const getUVCells = async (face, nrCellsU: number, nrCellsV: number) => { const cellsUV: CellUV[] = []; const stepU = 1 / nrCellsU; const stepV = 1 / nrCellsV; for (let u = 0; u < nrCellsU; u++) { for (let v = 0; v < nrCellsV; v++) { const uPar1 = u * stepU; const vPar1 = v * stepV; const uPar2 = (u + 1) * stepU; const vPar2 = v * stepV; const uPar3 = u * stepU; const vPar3 = (v + 1) * stepV; const uPar4 = (u + 1) * stepU; const vPar4 = (v + 1) * stepV; const uCenter = u * stepU + (stepU / 2); const vCenter = v * stepV + (stepV / 2); const cornerUV: CellUV = { ptUV1: [uPar1, vPar1], ptUV2: [uPar2, vPar2], ptUV3: [uPar3, vPar3], ptUV4: [uPar4, vPar4], center: [uCenter, vCenter], uIndex: u, vIndex: v, } cellsUV.push(cornerUV); } } const promises = cellsUV.map(c => { const allUVSPoints = [c.ptUV1, c.ptUV2, c.ptUV3, c.ptUV4, c.center]; return bitbybit.occt.shapes.face.pointsOnUVs({ shape: face, paramsUV: allUVSPoints }); }); let pts = await Promise.all(promises); const normalPromises = cellsUV.map(c => { const allUVSPoints = [c.ptUV1, c.ptUV2, c.ptUV3, c.ptUV4, c.center]; return bitbybit.occt.shapes.face.normalsOnUVs({ shape: face, paramsUV: allUVSPoints }); }); let normals = await Promise.all(normalPromises); const cellsPt: CellPt[] = pts.map((p, index) => { let cellPt: CellPt = { pt1: p[0], pt2: p[1], pt3: p[2], pt4: p[3], center: p[4], pt1Normal: normals[index][0], pt2Normal: normals[index][1], pt3Normal: normals[index][2], pt4Normal: normals[index][3], centerNormal: normals[index][4], uIndex: cellsUV[index].uIndex, vIndex: cellsUV[index].vIndex, } return cellPt; }) return cellsPt; } type TrianglesEntrance = { entranceSideFrames: ShapePointer[], entranceSideWindows: ShapePointer[] }; const createTrianglesEntrance = async (pt1: Point3, pt2: Point3, pt3: Point3): Promise<TrianglesEntrance> => { const entranceSideFrames = []; const entranceSideWindows = []; const ln1 = await bitbybit.occt.shapes.edge.line({ start: pt1, end: pt2 }); const ln2 = await bitbybit.occt.shapes.edge.line({ start: pt2, end: pt3 }); const ln3 = await bitbybit.occt.shapes.edge.line({ start: pt3, end: pt1 }); let triangleWire = await bitbybit.occt.shapes.wire.combineEdgesAndWiresIntoAWire({ shapes: [ln1, ln2, ln3] }); const plane = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: triangleWire, planar: true }); const normal = await bitbybit.occt.shapes.face.normalOnUV({ shape: plane, paramU: 0.5, paramV: 0.5 }); const normalized = bitbybit.vector.normalized({ vector: normal }); const offsetVec = bitbybit.vector.mul({ vector: normalized, scalar: 0.02 }) as Vector3; const offsetVec2 = bitbybit.vector.mul({ vector: normalized, scalar: 0.01 }) as Vector3; const offsetVec3 = bitbybit.vector.mul({ vector: normalized, scalar: -0.02 }) as Vector3; const triangleWire2 = await bitbybit.occt.fillets.fillet2d({ shape: triangleWire, radius: 0.08 }); const triangleWire3 = await bitbybit.occt.fillets.fillet2d({ shape: triangleWire, radius: 0.0001 }); const triangleWireMiddle1 = await bitbybit.occt.operations.offset({ shape: triangleWire2, distance: -0.03, tolerance: 1e-7 }); const triangleWireMiddle2 = await bitbybit.occt.operations.offset({ shape: triangleWire2, distance: -0.04, tolerance: 1e-7 }); const triangleWireMiddle3 = await bitbybit.occt.operations.offset({ shape: triangleWire2, distance: -0.05, tolerance: 1e-7 }); const triangleWireCenter = await bitbybit.occt.operations.offset({ shape: triangleWire2, distance: -0.07, tolerance: 1e-7 }); const triangleWireMiddleDown = await bitbybit.occt.operations.offset({ shape: triangleWire2, distance: -0.03, tolerance: 1e-7 }); const triangleWireMiddleUp1 = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddle1, translation: offsetVec }); const triangleWireMiddleUp2 = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddle2, translation: offsetVec2 }); const triangleWireMiddleUp3 = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddle3, translation: offsetVec }); const triangleWireMiddleDownMoved = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddleDown, translation: offsetVec3 }); const opt = new Bit.Inputs.OCCT.LoftAdvancedDto([triangleWire3, triangleWireMiddleUp1, triangleWireMiddleUp2, triangleWireMiddleUp3, triangleWireCenter, triangleWireMiddleDownMoved]); opt.closed = true; opt.straight = true; const loft = await bitbybit.occt.operations.loftAdvanced(opt); entranceSideFrames.push(loft); const window = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: triangleWireCenter, planar: true }); entranceSideWindows.push(window); return { entranceSideFrames, entranceSideWindows }; } type TrianglesSide = { sideFrames: ShapePointer[], sideWindows: ShapePointer[] }; const createTrianglesSide = async (pt1: Point3, pt2: Point3, pt3: Point3, pt3Normal: Vector3, centerOffsetFactor: number, inBetweenScaling: number): Promise<TrianglesSide> => { const sideFrames = []; const sideWindows = []; const centerNormal = bitbybit.vector.mul({ vector: pt3Normal, scalar: centerOffsetFactor }) as Bit.Inputs.Base.Vector3; const translation = bitbybit.babylon.transforms.translationXYZ({ translation: centerNormal }) const movedPt = await bitbybit.point.transformPoint({ point: pt3, transformation: translation }); const ln1 = await bitbybit.occt.shapes.edge.line({ start: pt1, end: pt2 }); const ln2 = await bitbybit.occt.shapes.edge.line({ start: pt2, end: movedPt }); const ln3 = await bitbybit.occt.shapes.edge.line({ start: movedPt, end: pt1 }); let triangleWire = await bitbybit.occt.shapes.wire.combineEdgesAndWiresIntoAWire({ shapes: [ln1, ln2, ln3] }); const plane = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: triangleWire, planar: true }); const normal = await bitbybit.occt.shapes.face.normalOnUV({ shape: plane, paramU: 0.5, paramV: 0.5 }); const center = await bitbybit.occt.shapes.face.getFaceCenterOfMass({ shape: plane }); const normalized = bitbybit.vector.normalized({ vector: normal }); const offsetVec = bitbybit.vector.mul({ vector: normalized, scalar: inBetweenScaling }) as Bit.Inputs.Base.Vector3; const offsetVec2 = bitbybit.vector.mul({ vector: normalized, scalar: -0.01 }) as Bit.Inputs.Base.Vector3; const triangleWireMiddle1 = await bitbybit.occt.operations.offset({ shape: triangleWire, distance: -0.03, tolerance: 1e-7 }); const triangleWireMiddle2 = await bitbybit.occt.operations.offset({ shape: triangleWire, distance: -0.04, tolerance: 1e-7 }); const triangleWireMiddle3 = await bitbybit.occt.transforms.scale3d({ shape: triangleWire, scale: [0.001, 0.001, 0.001], center }); const triangleWireMiddleUp2 = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddle2, translation: offsetVec2 }); const triangleWireMiddleUp3 = await bitbybit.occt.transforms.translate({ shape: triangleWireMiddle3, translation: offsetVec }); const opt = new LoftAdvancedDto([triangleWire, triangleWireMiddle1, triangleWireMiddleUp2]); opt.closed = true; opt.straight = true; opt.parType = approxParametrizationTypeEnum.approxCentripetal; opt.maxUDegree = 1; opt.periodic = false; opt.tolerance = 1e-7; const loft = await bitbybit.occt.operations.loftAdvanced(opt); sideFrames.push(loft); opt.shapes = [triangleWireMiddleUp2, triangleWireMiddleUp3]; opt.closed = false; const loft2 = await bitbybit.occt.operations.loftAdvanced(opt); sideWindows.push(loft2) return { sideFrames, sideWindows }; } const cells1 = await getUVCells(face1, 6, 25); const cells2 = await getUVCells(face1Of, 6, 25); const cells3 = await getUVCells(face2, 25, 6); const cells4 = await getUVCells(face2Of, 25, 6); const cells5 = await getUVCells(faceSide1, 25, 6); const cells6 = await getUVCells(faceSide2, 25, 6); const cells7 = await getUVCells(faceTop, 6, 6); const computeTriangleFramesForCells = async (cells: CellPt[], cellsOffset: CellPt[]): Promise<TrianglesEntrance[]> => { let index = 0; let triEnt: TrianglesEntrance[] = []; for (const cell of cells) { const cellTop = cellsOffset[index]; const triEnt1 = await createTrianglesEntrance(cell.pt1, cell.pt2, cellTop.center); const triEnt2 = await createTrianglesEntrance(cell.pt2, cell.pt4, cellTop.center); const triEnt3 = await createTrianglesEntrance(cell.pt4, cell.pt3, cellTop.center); const triEnt4 = await createTrianglesEntrance(cell.pt3, cell.pt1, cellTop.center); triEnt.push(...[triEnt1, triEnt2, triEnt3, triEnt4]); index++; } return triEnt; } const computeTriangleSidesForCells = async (cells: CellPt[], centerOffsetFactor: number, inBetweenScaling: number): Promise<TrianglesSide[]> => { let triSides: TrianglesSide[] = []; for (const cell of cells) { const triSide1 = await createTrianglesSide(cell.pt1, cell.pt2, cell.center, cell.centerNormal, centerOffsetFactor, inBetweenScaling); const triSide2 = await createTrianglesSide(cell.pt2, cell.pt4, cell.center, cell.centerNormal, centerOffsetFactor, inBetweenScaling); const triSide3 = await createTrianglesSide(cell.pt4, cell.pt3, cell.center, cell.centerNormal, centerOffsetFactor, inBetweenScaling); const triSide4 = await createTrianglesSide(cell.pt3, cell.pt1, cell.center, cell.centerNormal, centerOffsetFactor, inBetweenScaling); triSides.push(...[triSide1, triSide2, triSide3, triSide4]); } return triSides; } const tEntrance1 = await computeTriangleFramesForCells(cells1, cells2); const tEntrance2 = await computeTriangleFramesForCells(cells3, cells4); const tSide1 = await computeTriangleSidesForCells(cells5, 0.3, 0.04); const tSide2 = await computeTriangleSidesForCells(cells6, 0.3, 0.04); const tsTop3 = await computeTriangleSidesForCells(cells7, 0.2, 0.02); const chunkize = (shapes: ShapePointer[], chunkSize: number): ShapePointer[][] => { const chunks = []; for (let i = 0; i < shapes.length; i += chunkSize) { const chunk = shapes.slice(i, i + chunkSize); chunks.push(chunk); } return chunks; } const drawEntrances = async (t: TrianglesEntrance[]) => { const drawOptionsEntranceFrames = new DrawOcctShapeOptions(); drawOptionsEntranceFrames.edgeWidth = 0.2; drawOptionsEntranceFrames.precision = meshingPrecision; drawOptionsEntranceFrames.faceColour = '#8888ff'; drawOptionsEntranceFrames.edgeColour = '#000000'; drawOptionsEntranceFrames.drawEdges = drawEdges; const frames = t.map(s => s.entranceSideFrames).flat(); for (const chunk of chunkize(frames, chunksize)) { const compoundFrames = await bitbybit.occt.shapes.compound.makeCompound({ shapes: chunk }); await bitbybit.draw.drawAnyAsync({ entity: compoundFrames, options: drawOptionsEntranceFrames }); } const drawOptionsEntranceWindows = new DrawOcctShapeOptions(); drawOptionsEntranceWindows.edgeWidth = 0.2; drawOptionsEntranceWindows.precision = meshingPrecision; drawOptionsEntranceWindows.drawEdges = drawEdges; const windowMaterial = new PBRMetallicRoughnessDto(); windowMaterial.baseColor = '#0000ff'; windowMaterial.backFaceCulling = false; windowMaterial.metallic = 0.9; windowMaterial.roughness = 0.3; windowMaterial.alpha = 0.6; windowMaterial.zOffset = 2; drawOptionsEntranceWindows.faceMaterial = bitbybit.babylon.material.pbrMetallicRoughness.create(windowMaterial) const windows = t.map(s => s.entranceSideWindows).flat(); for (const chunk of chunkize(windows, chunksize)) { const compoundFrames = await bitbybit.occt.shapes.compound.makeCompound({ shapes: chunk }); await bitbybit.draw.drawAnyAsync({ entity: compoundFrames, options: drawOptionsEntranceWindows }); } } const drawSides = async (t: TrianglesSide[]) => { const drawOptionsSideFrames = new DrawOcctShapeOptions(); drawOptionsSideFrames.edgeWidth = 0.2; drawOptionsSideFrames.precision = meshingPrecision; drawOptionsSideFrames.faceColour = '#111155'; drawOptionsSideFrames.edgeColour = '#000000'; drawOptionsSideFrames.drawEdges = drawEdges; const frames = t.map(s => s.sideFrames).flat(); for (const chunk of chunkize(frames, chunksize)) { const compoundFrames = await bitbybit.occt.shapes.compound.makeCompound({ shapes: chunk }); await bitbybit.draw.drawAnyAsync({ entity: compoundFrames, options: drawOptionsSideFrames }); } const drawOptionsSidePanels = new DrawOcctShapeOptions(); drawOptionsSidePanels.edgeWidth = 0.2; drawOptionsSidePanels.precision = meshingPrecision; drawOptionsSidePanels.drawEdges = drawEdges; const panelMaterial = new PBRMetallicRoughnessDto(); panelMaterial.baseColor = '#111155'; panelMaterial.backFaceCulling = false; panelMaterial.metallic = 0.8; panelMaterial.roughness = 0.3; drawOptionsSidePanels.faceMaterial = bitbybit.babylon.material.pbrMetallicRoughness.create(panelMaterial) const windows = t.map(s => s.sideWindows).flat(); for (const chunk of chunkize(windows, chunksize)) { const compoundFrames = await bitbybit.occt.shapes.compound.makeCompound({ shapes: chunk }); await bitbybit.draw.drawAnyAsync({ entity: compoundFrames, options: drawOptionsSidePanels }); } } await drawEntrances(tEntrance1); await drawEntrances(tEntrance2); await drawSides(tSide1); await drawSides(tSide2); await drawSides(tsTop3); if (!exportGlb) { const groundOpt = new DrawOcctShapeOptions(); groundOpt.faceColour = '#5555ff'; groundOpt.drawEdges = false; const ground = await bitbybit.occt.shapes.face.createCircleFace({ radius: 20, center: [0, 0, 0], direction: [0, 1, 0] }) await bitbybit.draw.drawAnyAsync({ entity: ground, options: groundOpt }); } if (exportGlb) { bitbybit.babylon.io.exportGLB({ fileName: 'parametric-structural-tower-facade.glb' }) } } start();