Animation tests

MAAS to Machine with NIC Status Indicators body { margin: 0; } canvas { display: block; } .slider-container { position: absolute; bottom: 370px; left: 50%; transform: translateX(-50%); width: 40%; }
<script>
    // Set up scene, camera, renderer
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf2f2f2);  // Ghostly light gray background color

    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // Utility to create text label as a sprite
    function createTextSprite(message, x, y, z, widthFactor = 1.0) {
        const canvas = document.createElement('canvas');
        canvas.width = 500;
        canvas.height = 200;

        const context = canvas.getContext('2d');
        context.font = 'bold 48px Arial';  // Normal text size
        context.fillStyle = 'black';  // Black text color
        context.textAlign = 'center';
        context.textBaseline = 'middle';
        context.fillText(message, canvas.width / 2, canvas.height / 2);

        const texture = new THREE.CanvasTexture(canvas);
        const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
        const sprite = new THREE.Sprite(spriteMaterial);

        // Scale sprite width according to the width factor (for centering over the box width)
        sprite.scale.set(widthFactor, 2.0, 2);  // Proportional height for normal text appearance
        sprite.position.set(x, y, z);
        scene.add(sprite);
    }

    // Create NIC box
    function createNICBox(x, y, z) {
        const nicGroup = new THREE.Group();

        // NIC base box (darker gray)
        const boxGeometry = new THREE.BoxGeometry(1.8, 0.5, 0.5);  // Wider NIC box
        const darkGrayMaterial = new THREE.MeshBasicMaterial({ color: 0xb0b0b0 });  // Darker gray fill
        const nicBox = new THREE.Mesh(boxGeometry, darkGrayMaterial);
        nicGroup.add(nicBox);

        // Green indicator circle (always on)
        const greenCircleGeometry = new THREE.CircleGeometry(0.15, 32);
        const greenMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const greenCircle = new THREE.Mesh(greenCircleGeometry, greenMaterial);
        greenCircle.position.set(-0.2, 0.0, 0.26);  // Bottom left of NIC box
        nicGroup.add(greenCircle);

        // Black status circle (turns yellow later)
        const blackCircleGeometry = new THREE.CircleGeometry(0.15, 32);
        const blackMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
        const blackCircle = new THREE.Mesh(blackCircleGeometry, blackMaterial);
        blackCircle.position.set(-0.6, 0.0, 0.26);  // Slightly to the left of the green circle
        nicGroup.add(blackCircle);

        nicGroup.position.set(x, y, z);
        scene.add(nicGroup);

        // Add "NIC" label (half the width of the NIC box)
        createTextSprite('NIC', x - 0.5, y + 0.45, z, 4.0);  // NIC label centered with half-width

        return { nicGroup, blackCircle };
    }

    // Function to create a server box with vents and lamp
    function createServerBox(x, y, z, label, lampAlwaysGreen) {
        const boxGroup = new THREE.Group();

        // Outer box (gray)
        const boxWidth = 1.5;
        const outerGeometry = new THREE.BoxGeometry(boxWidth, 1, 1);
        const outerMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });
        const outerBox = new THREE.Mesh(outerGeometry, outerMaterial);

        // On-lamp
        const lampGeometry = new THREE.BoxGeometry(0.3, 0.1, 0.01);
        const lampColor = lampAlwaysGreen ? 0x00ff00 : 0x003300;
        const lampMaterial = new THREE.MeshBasicMaterial({ color: lampColor });
        const lamp = new THREE.Mesh(lampGeometry, lampMaterial);
        lamp.position.set(0, -0.4, 0.51);  // Slightly outside the front face

        // Vent lines
        const ventGroup = new THREE.Group();
        const ventMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
        for (let i = -0.6; i <= 0.6; i += 0.2) {
            const ventGeometry = new THREE.BoxGeometry(0.05, 0.8, 0.01);
            const vent = new THREE.Mesh(ventGeometry, ventMaterial);
            vent.position.set(i, 0, 0.51);
            ventGroup.add(vent);
        }

        boxGroup.add(outerBox);
        boxGroup.add(ventGroup);
        boxGroup.add(lamp);
        boxGroup.position.set(x, y, z);
        scene.add(boxGroup);

        // Add label centered and closer to the server box
        const widthFactor = label === "NIC" ? 1.5 : 5.0;  // Width factor for normal labels
        createTextSprite(label, x, y + 0.85, z, widthFactor);  // Centered and proportional text

        return { boxGroup, lamp };
    }

    const maasServer = createServerBox(-3, 0, 0, "MAAS", true);  // MAAS always green
    const machineServer = createServerBox(3, 0, 0, "Machine", false);  // Machine starts off
    const nicBox = createNICBox(2.1, 0, 0);  // NIC positioned closer and wider

    // Wire between MAAS and NIC
    const wireGeometry = new THREE.CylinderGeometry(0.05, 0.05, 5.6);
    const wireMaterial = new THREE.MeshBasicMaterial({ color: 0x444444 });
    const wire = new THREE.Mesh(wireGeometry, wireMaterial);
    wire.rotation.z = Math.PI / 2;
    wire.position.set(-0.5, 0, 0);
    scene.add(wire);

    // Pulse (sphere representing data transfer)
    const pulseGeometry = new THREE.SphereGeometry(0.15, 32, 32);
    const pulseMaterial = new THREE.MeshBasicMaterial({ color: 0xFFCC00 });
    const pulse = new THREE.Mesh(pulseGeometry, pulseMaterial);
    pulse.position.set(-3, 0, 0);  // Start at MAAS
    scene.add(pulse);

    camera.position.z = 8;

    // Function to update the pulse position and status lights
    let nicGreen = false;
    let machineGreen = false;

    function updatePulsePosition(value) {
        const startX = -3;
        const endX = 2.1;
        const newX = startX + (value / 100) * (endX - startX);
        pulse.position.x = newX;

        // Turn NIC status light yellow when pulse arrives
        if (newX >= 1.4) {
            nicBox.blackCircle.material.color.setHex(0xFFCC00);  // Yellow status light
            nicGreen = true;

            // Turn Machine lamp green after 1 second
            setTimeout(() => {
                machineServer.lamp.material.color.setHex(0x00ff00);  // Green Machine lamp
                machineGreen = true;
            }, 1000);
        }
    }

    // Slider listener
    const slider = document.getElementById('slider');
    slider.addEventListener('input', (event) => {
        updatePulsePosition(event.target.value);
    });

    // Render loop
    function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
    }

    animate();

    window.addEventListener('resize', () => {
        renderer.setSize(window.innerWidth, window.innerHeight);
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
    });

</script>