type TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;
start();
async function start() {
const hexRadius = 20;
const innerSideLength = 1.5;
const nrHexagonsX = 6;
const nrHexagonsY = 7;
const cornerFilletRadiusForBase = 0.6;
createLightsAndAdjustCamera();
const trianglePoints = createTrianglePoints(hexRadius);
const baseFlatTriangle = await baseSetupForHexagonTriangleShape(trianglePoints);
const hexGrid = makeHexGrid(nrHexagonsX, nrHexagonsY, hexRadius);
const linesForIntersection = verticalLinesForIntersection(hexGrid);
const nurbsSurface = createNurbsSurfaceForHexGridProjections(hexGrid, nrHexagonsY);
const pointsOnSurface = findIntersectionsOnSurface(linesForIntersection, nurbsSurface);
const pointsAboveSurface = bitbybit.point.transformPoints({
points: pointsOnSurface,
transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 15, 0] })
});
const columns = bitbybit.line.linesBetweenStartAndEndPoints({
startPoints: hexGrid,
endPoints: pointsAboveSurface
});
const bulbBox = await bitbybit.occt.shapes.solid.createBox({ width: 1, length: 1, height: 4, center: [0, 0, 0] });
const loftSolid = await createLoftSolid(trianglePoints[1], trianglePoints[2]);
const innerVoidSolid = await createSolidForDifference(-innerSideLength, baseFlatTriangle);
const baseTriangleSolid = await findDifferenceAndFilletResult(loftSolid, innerVoidSolid, cornerFilletRadiusForBase);
const chamferedGlassSolid = await createChamferGlassSolid(baseTriangleSolid);
const hexagonsBase = await rotateTriangles(baseTriangleSolid, 0, 300, 60);
const hexagonsGlass = await rotateTriangles(chamferedGlassSolid, 0, 300, 60);
const ground = await bitbybit.occt.transforms.translate({
shape: await bitbybit.occt.shapes.solid.createBox({ width: 500, length: 500, height: 1, center: [0, 0, 0] }),
translation: [125, 0, 125]
})
drawEverything(hexagonsBase, hexagonsGlass, bulbBox, ground, pointsOnSurface, pointsAboveSurface, columns);
}
function createLightsAndAdjustCamera() {
bitbybit.babylon.scene.adjustActiveArcRotateCamera({ position: [250, 170, 250], lookAt: [0, 0, 0], maxZ: 1000, wheelPrecision: 3, panningSensibility: 100 });
const lightOpts = { diffuse: '#ff99ff', specular: '#ffffff', radius: 0.5, intensity: 60000 };
bitbybit.babylon.scene.drawPointLight({ position: [200, 130, 200], ...lightOpts });
bitbybit.babylon.scene.drawPointLight({ position: [0, 160, 0], ...lightOpts });
bitbybit.babylon.scene.drawPointLight({ position: [200, 130, 200], ...lightOpts });
}
async function baseSetupForHexagonTriangleShape(trianglePoints: Bit.Inputs.Base.Point3[]): Promise<TopoDSShapePointer> {
return await bitbybit.occt.shapes.wire.createPolygonWire({ points: trianglePoints });
}
function createTrianglePoints(length: number): Bit.Inputs.Base.Point3[] {
const lineOrient = bitbybit.line.create({ start: [0, 0, 0], end: [0, 0, length] });
const point1 = [0, 0, 0] as Bit.Inputs.Base.Point3;
const point2 = bitbybit.line.getEndPoint({
line: bitbybit.line.transformLine({
line: lineOrient,
transformation: bitbybit.babylon.transforms.rotationCenterY({ center: [0, 0, 0], angle: 59.99 })
})
});
const point3 = bitbybit.line.getEndPoint({
line: bitbybit.line.transformLine({
line: lineOrient,
transformation: bitbybit.babylon.transforms.rotationCenterY({ center: [0, 0, 0], angle: 0.01 })
})
});
return [point1, point2, point3];
}
function makeHexGrid(nrHexagonsX: number, nrHexagonsY: number, radiusHexagon: number) {
return bitbybit.point.transformPoints({
points: bitbybit.point.hexGrid({ nrHexagonsX, nrHexagonsY, radiusHexagon }),
transformation: bitbybit.babylon.transforms.rotationCenterX({ angle: 90, center: [0, 0, 0] })
});
}
function verticalLinesForIntersection(hexGrid: Bit.Inputs.Base.Point3[]) {
return bitbybit.line.linesBetweenStartAndEndPoints({
startPoints: hexGrid,
endPoints: bitbybit.point.transformPoints({
points: hexGrid,
transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 200, 0] })
})
});
}
function createNurbsSurfaceForHexGridProjections(hexGridPoints: Bit.Inputs.Base.Point3[], nrHexagonsY: number) {
const surface = bitbybit.verb.surface.createSurfaceByCorners({
point1: bitbybit.point.transformPoint({
point: hexGridPoints[0], transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 100, 0] }),
}),
point2: bitbybit.point.transformPoint({
point: hexGridPoints[nrHexagonsY - 1], transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 50, 0] }),
}),
point3: bitbybit.point.transformPoint({
point: hexGridPoints[hexGridPoints.length - 1], transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 90, 0] }),
}),
point4: bitbybit.point.transformPoint({
point: hexGridPoints[hexGridPoints.length - nrHexagonsY], transformation: bitbybit.babylon.transforms.translationXYZ({ translation: [0, 40, 0] }),
}),
});
return bitbybit.verb.surface.transformSurface({
surface,
transformation: [
bitbybit.babylon.transforms.scaleXYZ({ scaleXyz: [1.5, 1, 1.5] }),
bitbybit.babylon.transforms.translationXYZ({ translation: [-20, 0, -20] })
]
})
}
function findIntersectionsOnSurface(linesForIntersection: Bit.Inputs.Line.LinePointsDto[], surface: number): Bit.Inputs.Base.Point3[] {
return linesForIntersection.map(line => {
const curve = bitbybit.line.convertToNurbsCurve({ line });
const intersection = bitbybit.verb.intersect.curveAndSurface({ curve, surface });
let result;
if (intersection.length > 0) {
result = intersection[0].curvePoint;
} else {
result = line.start;
}
return result;
})
}
async function createLoftSolid(point2: number[], point3: number[]): Promise<TopoDSShapePointer> {
const heightOfHexagon = 14;
const tiltedBottomTriangleWire = await bitbybit.occt.shapes.wire.createPolygonWire({
points: [
[0, 5, 0],
[point2[0], 0, point2[2]],
[point3[0], 0, point3[2]],
]
});
const tiltedTopTriangleWire = await bitbybit.occt.shapes.wire.createPolygonWire({
points: [
[0, 10, 0],
[point2[0], heightOfHexagon, point2[2]],
[point3[0], heightOfHexagon, point3[2]],
]
});
return await bitbybit.occt.operations.loft({ shapes: [tiltedBottomTriangleWire, tiltedTopTriangleWire], makeSolid: true });
}
async function createSolidForDifference(offset: number, baseTriangle: TopoDSShapePointer): Promise<TopoDSShapePointer> {
const offTriangleWire = await bitbybit.occt.operations.offset({ shape: baseTriangle, distance: offset, tolerance: 0.00001 });
const offTraingleFace = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: offTriangleWire, planar: true });
return await bitbybit.occt.operations.extrude({ shape: offTraingleFace, direction: [0, 50, 0] });
}
async function findDifferenceAndFilletResult(outerSolid, innerSolid, radius): Promise<TopoDSShapePointer> {
const hollowSolid = await bitbybit.occt.booleans.difference({ shape: outerSolid, shapes: [innerSolid], keepEdges: true });
return await bitbybit.occt.fillets.filletEdges({ shape: hollowSolid, radius, indexes: undefined });
}
async function createChamferGlassSolid(baseTriangleSolid: TopoDSShapePointer): Promise<TopoDSShapePointer> {
const edgeIndexesToChamfer = [15, 12, 3];
const faceIndexForWire = 14;
const chamferDist = 0.5;
const wireOfFilletShape = await bitbybit.occt.shapes.wire.getWire({ shape: baseTriangleSolid, index: faceIndexForWire });
const faceOfTheWire = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: wireOfFilletShape, planar: true });
const extruded = await bitbybit.occt.operations.extrude({ shape: faceOfTheWire, direction: [0, 1, 0] });
return await bitbybit.occt.fillets.chamferEdges({ shape: extruded, distance: chamferDist, indexes: edgeIndexesToChamfer });
}
async function rotateTriangles(shape: TopoDSShapePointer, angleFrom: number, angleTo: number, angleStep: number): Promise<TopoDSShapePointer[]> {
let result = [];
for (let angle = angleFrom; angle <= angleTo; angle += angleStep) {
const rotated = await bitbybit.occt.transforms.rotate({
shape,
axis: [0, 1, 0],
angle
});
result.push(rotated);
}
return result;
}
async function drawEverything(
hexagonsBase: Bit.Inputs.OCCT.TopoDSShapePointer[],
hexagonsGlass: Bit.Inputs.OCCT.TopoDSShapePointer[],
bulbBox: Bit.Inputs.OCCT.TopoDSShapePointer,
ground: Bit.Inputs.OCCT.TopoDSShapePointer,
pointsOnHexagonSurface: Bit.Inputs.Base.Point3[],
pointsAboveSurface: Bit.Inputs.Base.Point3[],
columns,
): Promise<void> {
const whiteColour = '#ffffff';
const violetColour = '#993399';
const goldColour = '#ffcc33';
const blueColour = '#3366ff';
const blackColour = '#000000';
const lineDrawOptions = new Bit.Inputs.Line.DrawLinesDto(columns);
lineDrawOptions.size = 50;
lineDrawOptions.opacity = 0.5;
lineDrawOptions.colours = whiteColour;
bitbybit.line.drawLines(lineDrawOptions);
const hexagonsBaseInPosition = await Promise.all(hexagonsBase.map(hexagonBase => {
return bitbybit.occt.transforms.translate({
shape: hexagonBase,
translation: pointsOnHexagonSurface[0],
})
}));
const hexagonsGlassInPosition = await Promise.all(hexagonsGlass.map(hexagonGlass => {
return bitbybit.occt.transforms.translate({
shape: hexagonGlass,
translation: pointsOnHexagonSurface[0],
})
}));
const drawingOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();
drawingOptions.precision = 1;
drawingOptions.faceColour = violetColour;
drawingOptions.edgeColour = whiteColour;
drawingOptions.edgeWidth = 5;
await bitbybit.draw.drawAny({ entity: hexagonsBaseInPosition[0], options: drawingOptions });
drawingOptions.faceColour = goldColour;
drawingOptions.faceOpacity = 0.3;
drawingOptions.drawEdgeIndexes = true;
drawingOptions.edgeIndexHeight = 0.15;
drawingOptions.edgeWidth = 3;
drawingOptions.edgeIndexColour = whiteColour;
await bitbybit.draw.drawAnyAsync({ entity: hexagonsGlassInPosition[0], options: drawingOptions });
const fiveTriangleBasesOnFirsHexagon = await bitbybit.occt.shapes.compound.makeCompound({ shapes: hexagonsBaseInPosition.filter((_, index) => index !== 0) });
drawingOptions.faceColour = violetColour;
drawingOptions.faceOpacity = 0.2;
drawingOptions.drawEdgeIndexes = false;
drawingOptions.edgeWidth = 3;
drawingOptions.edgeOpacity = 0.6;
await bitbybit.draw.drawAnyAsync({ entity: fiveTriangleBasesOnFirsHexagon, options: drawingOptions });
const compoundRemainderBases = await bitbybit.occt.shapes.compound.makeCompound({ shapes: hexagonsBase });
const compoundRemainderGlasses = await bitbybit.occt.shapes.compound.makeCompound({ shapes: hexagonsGlass });
drawingOptions.precision = 40;
drawingOptions.drawEdges = false;
drawingOptions.faceColour = blueColour;
drawingOptions.faceOpacity = 1;
const meshBase = await bitbybit.draw.drawAnyAsync({ entity: compoundRemainderBases, options: drawingOptions });
drawingOptions.faceColour = goldColour;
drawingOptions.faceOpacity = 0.3;
const meshGlass = await bitbybit.draw.drawAnyAsync({ entity: compoundRemainderGlasses, options: drawingOptions });
const pointsOnWhichToDrawRemainder = pointsOnHexagonSurface.filter((_, index) => index !== 0);
pointsOnWhichToDrawRemainder.forEach(point => {
bitbybit.babylon.mesh.createMeshInstanceAndTransform({
mesh: meshBase,
position: point,
rotation: [0, 0, 0],
scaling: [1, 1, 1],
});
bitbybit.babylon.mesh.createMeshInstanceAndTransform({
mesh: meshGlass,
position: point,
rotation: [0, 0, 0],
scaling: [1, 1, 1],
});
});
const boxDrawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();
boxDrawOptions.drawEdges = false;
boxDrawOptions.faceColour = whiteColour;
const meshBulbBox = await bitbybit.draw.drawAnyAsync({ entity: bulbBox, options: boxDrawOptions });
pointsAboveSurface.forEach(point => {
bitbybit.babylon.mesh.createMeshInstanceAndTransform({
mesh: meshBulbBox,
position: point,
rotation: [0, 0, 0],
scaling: [1, 1, 1],
})
});
const groundDrawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();
groundDrawOptions.drawEdges = false;
groundDrawOptions.faceOpacity = 0.99;
groundDrawOptions.faceColour = blackColour;
await bitbybit.draw.drawAnyAsync({ entity: ground, options: groundDrawOptions });
}