Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building Interactive 3D Hospital Navigation Systems with WebGL and A* Pathfinding

Tech 1

3D Architecture and Procedural Modeling

Implementing efficient navigation within a hospital facility requires a balance between visual fidelity and performance, particularly on mobile platforms. Instead of relying on heavy external model files, the 3D environment is generated procedurally using Three.js. This approach minimizes load times and optimizes frame rates. By defining geometries and materials at runtime, the system allows for flexible adaptation to different hospital layouts without significant resource overhead.

Dynamic Spatial Labeling

To assist users in identifying specific departments, a dynamic labeling system is employed. This involves attaching 3D coordinates to specific UI elements or markers that overlay the scene. When a user interacts with a department, the system calculates the world position of the target mesh and updates the label's position to float above it, ensuring visibility regardless of camera movement.

class LabelManager {
    constructor(scene) {
        this.scene = scene;
        this.activeLabel = null;
    }

    updateLabelMarker(targetId, offsetHeight = 2.5) {
        const targetMesh = this.scene.getObjectByName(targetId);
        if (!targetMesh) return;

        const labelMesh = this.scene.getObjectByName("nav_marker");
        if (!labelMesh) return;

        // Get global position of the target
        const worldPos = new THREE.Vector3();
        targetMesh.getWorldPosition(worldPos);

        // Apply offset based on target type
        const isFlatPanel = targetId.includes("panel");
        labelMesh.position.copy(worldPos);
        labelMesh.position.y += isFlatPanel ? -0.1 : offsetHeight;
    }
}

3D Pathfinding Implementation

The core of the navigation system relies on calculating the most efficient route between the user's location and the destination. We utilize a modified A* (A-Star) search algorithm adapted for a three-dimensional grid. This algorithm traverses the navigable nodes in the 3D space, avoiding obstacles such as walls or restricted areas, to generate a precise path.

class Pathfinder {
    constructor() {
        this.directions = [];
        // Generate 26 neighbor directions for 3D movement
        for(let x=-1; x<=1; x++) {
            for(let y=-1; y<=1; y++) {
                for(let z=-1; z<=1; z++) {
                    if(x===0 && y===0 && z===0) continue;
                    this.directions.push({x, y, z});
                }
            }
        }
    }

    calculatePath(startVec, endVec, barrierSet) {
        const startNode = { pos: startVec, g: 0, h: 0, f: 0, parent: null };
        const endNode = { pos: endVec };
        
        let openList = [startNode];
        let closedSet = new Set();

        while (openList.length > 0) {
            // Sort by lowest F cost
            openList.sort((a, b) => a.f - b.f);
            const current = openList.shift();

            // Check if reached destination
            if (this.isEqual(current.pos, endNode.pos)) {
                return this.reconstructPath(current);
            }

            closedSet.add(this.getKey(current.pos));

            // Explore neighbors
            for (let dir of this.directions) {
                const neighborPos = {
                    x: Math.round(current.pos.x + dir.x),
                    y: Math.round(current.pos.y + dir.y),
                    z: Math.round(current.pos.z + dir.z)
                };

                const neighborKey = this.getKey(neighborPos);

                if (barrierSet.has(neighborKey) || closedSet.has(neighborKey)) continue;

                const dist = Math.sqrt(dir.x**2 + dir.y**2 + dir.z**2);
                const tentativeG = current.g + dist;

                let neighborNode = openList.find(n => this.isEqual(n.pos, neighborPos));

                if (!neighborNode) {
                    neighborNode = {
                        pos: neighborPos,
                        g: tentativeG,
                        h: this.heuristic(neighborPos, endNode.pos),
                        parent: current
                    };
                    neighborNode.f = neighborNode.g + neighborNode.h;
                    openList.push(neighborNode);
                } else if (tentativeG < neighborNode.g) {
                    neighborNode.g = tentativeG;
                    neighborNode.f = neighborNode.g + neighborNode.h;
                    neighborNode.parent = current;
                }
            }
        }
        return null;
    }

    heuristic(posA, posB) {
        return Math.sqrt(
            Math.pow(posA.x - posB.x, 2) +
            Math.pow(posA.y - posB.y, 2) +
            Math.pow(posA.z - posB.z, 2)
        );
    }

    getKey(pos) {
        return `${pos.x},${pos.y},${pos.z}`;
    }

    isEqual(v1, v2) {
        return v1.x === v2.x && v1.y === v2.y && v1.z === v2.z;
    }

    reconstructPath(node) {
        const path = [];
        let temp = node;
        while (temp) {
            path.unshift([temp.pos.x, temp.pos.y, temp.pos.z]);
            temp = temp.parent;
        }
        return path;
    }
}

Audio Guidance Integration

To enhance accessibility, the system includes text-to-speech capabilities. The solution prioritizes the browser's native Web Speech API for real-time feedback. If local synthesis is unavailable or specific voice quality is required, it falls back to cloud-based text-to-speech services. This ensures that users receive turn-by-turn audio instructions regardless of their device capabilities.

class AudioNavigator {
    constructor() {
        this.synth = window.speechSynthesis;
    }

    announce(message) {
        if (!this.synth) {
            console.warn("Speech Synthesis not supported");
            return;
        }

        // Cancel any currently playing speech
        this.synth.cancel();

        const utterance = new SpeechSynthesisUtterance(message);
        utterance.lang = 'en-US'; // Set locale dynamically as needed
        utterance.rate = 1.0;
        utterance.pitch = 1.0;

        this.synth.speak(utterance);
    }
}
Tags: Three.js

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.