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();