Script: Example in TypeScript

Example in TypeScript picture
Type
Typescript logo indicatortypescript
Date Created
Mar 19, 2021, 2:29:25 PM
Last Edit Date
Dec 5, 2023, 7:02:15 PM

Project Information

This example is showing off the possibilities of Async OpenCascade components and advanced rendering of "Bit by bit developers" application. You might need to have a more powerful GPU to have the scene rendered at good frame rates.

View Full Project

Script Code

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 });
}
Plans & Pricing

Choose Your Plan

Editor plans for 3D development, API keys for server-side CAD algorithms

B2B

ENTERPRISE

Custom pricing

Custom software development, dedicated servers & CAD automation at scale.

CAD Automation & Software
  • Custom software development
  • Cloud CAD automation pipelines
  • 3D configurators (STEP & GLTF)
  • Batch export jobs
  • Custom algorithms & deployment
Infrastructure & Support
  • Custom compute allocation
  • Dedicated / VPS server tenants
  • Long-running computation jobs
  • Custom upload limits & overage
  • SLA & premium support