corrade-nucleus-nucleons – Rev 36

Subversion Repositories:
Rev:
<div id="compass">
    <!-- Three.js -->
    <script src="/compass/js/three/three.min.js" type="text/javascript"></script>
    <!-- Helvetiker -->
    <script src="/compass/js/three/helvetiker_regular.typeface.js" type="text/javascript"></script>
    <!-- Orbit Controls -->
    <script src="/compass/js/three/OrbitControls.min.js" type="text/javascript"></script>

    <div class="panel panel-default window-manager-window">
      <div class="panel-heading">
        <button type="button" class="close window-manager-close-button" data-target="compass"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
        <h3 class="panel-title">Compass</h3>
      </div>
      <div class="panel-body">
        <div id="container"></div>
      </div>
    </div>
    
    <!-- Terrain Vertex and Fragment Shaders -->
    <!-- Vertex Shader -->
    <script id="terrainVertex" type="x-shader/x-vertex">
        uniform sampler2D bumpTexture;
        uniform float bumpScale;

        varying vec2 vUV;

        void main() {
            vUV = uv;
            vec4 bumpData = texture2D(bumpTexture, uv);
            
            // move the position along the normal
            vec3 newPosition = 
                position + 
                normal * 
                bumpScale * 
                bumpData.r; // use red channel (all equal in greyscale)

            gl_Position = 
                projectionMatrix * 
                modelViewMatrix * 
                vec4(newPosition, 1.0);
        }
    </script>
    <!-- Fragment Shader / Pixel Shader -->
    <script id="terrainFragment" type="x-shader/x-vertex"> 
        uniform sampler2D mapTexture;
        varying vec2 vUV;

        void main() {
            vec4 map = texture2D(mapTexture, vUV);
            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0)  + map; //, 1.0);
        }  
    </script>
    <!-- -->

    <script>
        
        /* The bounds of the plot. */
        var plotBounds = {
              'maxx' : 256,
              'minx' : -256,
              'maxy' : 256,
              'miny' : -256,
              'maxz' : 256,
              'minz' : -256
        };
        
        /* This represents the bounds of the data to plot.
         * These values will be linearly mapped onto the 
         * bounds for the plot.
         */
        var dataBounds = {
            'maxx' : 256,
            'minx' : 0,
            'maxy' : 256,
            'miny' : 0,
            'maxz' : 256, // Second Life maximum rez altitude
            'minz' : 0
        }
        
        /* Structure to be filled by the backend scripts. */
        var csv =
            {
                'header' : [ "x", "y", "z" ],
                'positions' : [],
                'names' : [],
                'colors' : []
            };
        
        /* The size of the cursor for each avatar. */
        var dotSize = 8;
        /* The interval in milliseconds for updating the terrain. */
        var mapFetchInterval = 60000;
        /* The interval in milliseconds for avatar position updates. */
        var avatarsFetchInterval = 1000;
        
        var container;
        var camera, controls, scene, renderer;
        var scatterPlot;
        var points;
        var avatarLabels = [];
        var avatarProjections = [];
        var mapImageData = null;
        var mapBumpData = null;
        var mapHeight;
        var mapMesh;
        
        init();
        animate();

        function init() {
            /* Create the camera. */
            camera = new THREE.PerspectiveCamera(
                60,
                window.innerWidth / window.innerHeight, 
                2,
                10000
            );
            camera.position.z = plotBounds.maxz * 2;
            camera.position.x = 0;
            camera.position.y = plotBounds.maxy * 1.25;
            controls = new THREE.OrbitControls(camera);
            controls.damping = 0.2;
            controls.addEventListener('change', render);

            /* Create the scene. */
            scene = new THREE.Scene();
            
            /* Add the scatterplot to it. */
            scatterPlot = new THREE.Object3D();
            scene.add(scatterPlot);

            /* Draw axes. */
            var xAxisGeo = new THREE.Geometry();
            var yAxisGeo = new THREE.Geometry();
            var zAxisGeo = new THREE.Geometry();

            xAxisGeo.vertices.push(
                new THREE.Vector3(
                        plotBounds.minx/2, 
                        plotBounds.miny/2, 
                        plotBounds.minz/2
                    ), 
                new THREE.Vector3(
                    plotBounds.maxx/2, 
                    plotBounds.miny/2, 
                    plotBounds.minz/2
                    )
            );
            yAxisGeo.vertices.push(
                new THREE.Vector3(
                    plotBounds.minx/2, 
                    plotBounds.miny/2, 
                    plotBounds.minz/2
                ), 
                new THREE.Vector3(
                    plotBounds.minx/2, 
                    plotBounds.maxy/2, 
                    plotBounds.minz/2
                )
            );
            zAxisGeo.vertices.push(
                new THREE.Vector3(
                    plotBounds.minx/2, 
                    plotBounds.miny/2, 
                    plotBounds.minz/2
                ), 
                new THREE.Vector3(
                    plotBounds.minx/2, 
                    plotBounds.miny/2, 
                    plotBounds.maxz/2
                    )
            );

            var xAxisMat = new THREE.LineBasicMaterial(
                {
                    color: 0xa60000, 
                    lineWidth: 1
                }
            );
            var xAxis = new THREE.Line(xAxisGeo, xAxisMat);
            xAxis.type = THREE.Lines;
            scatterPlot.add(xAxis);

            var yAxisMat = new THREE.LineBasicMaterial(
                {
                    color: 0x0000a6, 
                    lineWidth: 1
                }
            );
            var yAxis = new THREE.Line(yAxisGeo, yAxisMat);
                  yAxis.type = THREE.Lines;
                  scatterPlot.add(yAxis);

            var zAxisMat = new THREE.LineBasicMaterial(
                {
                    color: 0x00a600, 
                    lineWidth: 1
                }
            );
            var zAxis = new THREE.Line(zAxisGeo, zAxisMat);
            zAxis.type = THREE.Lines;
            scatterPlot.add(zAxis);

            /* Add axes text. */
            var text3d = new THREE.TextGeometry("O", {
                size: 8,
                height: 2,
                curveSegments: 2//,
                //font: "cmutypewriter_regular"

            });
            text3d.computeBoundingBox();
            var centerOffset = -0.5 * (
                text3d.boundingBox.max.x - 
                text3d.boundingBox.min.x
            );
            var textMaterial = new THREE.MeshBasicMaterial(
                { 
                    color: 0xa6a6a6, 
                    overdraw: 0.5 
                } 
            );
            text = new THREE.Mesh(text3d, textMaterial);
            text.position.x = plotBounds.minx/2 + 2 * centerOffset;
            text.position.y = plotBounds.minx/2 + 2 * centerOffset;
            text.position.z = plotBounds.minx/2;
            scatterPlot.add(text);
        
            text3d = new THREE.TextGeometry(
                csv.header[0] + 
                " " + 
                "(" + 
                dataBounds.maxx + 
                "m)", 
                {
                    size: 8,
                    height: 2,
                    curveSegments: 2//,
                    //font: "cmutypewriter_regular"
                }
            );
            text3d.computeBoundingBox();
            centerOffset = -0.5 * (
                text3d.boundingBox.max.x - 
                text3d.boundingBox.min.x 
            );
            textMaterial = new THREE.MeshBasicMaterial(
                { 
                    color: 0xa60000, 
                    overdraw: 0.5
                }
            );
            text = new THREE.Mesh(text3d, textMaterial);
            text.position.x = plotBounds.maxx/2 - centerOffset / 4;
            text.position.y = plotBounds.miny/2 + centerOffset / 8;
            text.position.z = plotBounds.minz/2;
            scatterPlot.add(text);

            text3d = new THREE.TextGeometry(
                csv.header[2] + 
                " " + 
                "(" + 
                dataBounds.maxz + 
                "m)", 
                {
                    size: 8,
                    height: 2,
                    curveSegments: 2//,
                    //font: "cmutypewriter_regular"
                }
            );
            text3d.computeBoundingBox();
            centerOffset = -0.5 * (
                text3d.boundingBox.max.x - 
                text3d.boundingBox.min.x
            );
            textMaterial = new THREE.MeshBasicMaterial(
                {
                    color: 0x0000a6, 
                    overdraw: 0.5 
                }
            );
            text = new THREE.Mesh( text3d, textMaterial );
            text.position.x = plotBounds.minx/2 + centerOffset;
            text.position.y = plotBounds.maxy/2 - centerOffset / 4;
            text.position.z = plotBounds.minz/2;
            scatterPlot.add(text);

            text3d = new THREE.TextGeometry(
                csv.header[1] + 
                " " + 
                "(" + 
                dataBounds.maxy + 
                "m)", 
                {
                    size: 8,
                    height: 2,
                    curveSegments: 2//,
                    //font: "cmutypewriter_regular"
                }
            );
            text3d.computeBoundingBox();
            centerOffset = -0.5 * (
                text3d.boundingBox.max.x -
                text3d.boundingBox.min.x
            );
            textMaterial = new THREE.MeshBasicMaterial(
                {
                    color: 0x00a600,
                    overdraw: 0.5 
                }
            );
            text = new THREE.Mesh(text3d, textMaterial);
            text.position.x = plotBounds.minx/2 + centerOffset;
            text.position.y = plotBounds.miny/2;
            text.position.z = plotBounds.maxz/2 - centerOffset / 4;
            scatterPlot.add(text);
            
            /* Add enclosing case. */
            var enclosingCubeGeometry = enclosingCase(plotBounds.maxx);
            enclosingCubeGeometry.computeLineDistances();
            var enclosingCube = 
                new THREE.Line(
                    enclosingCubeGeometry, 
                    new THREE.LineDashedMaterial(
                        {
                            color: 0xa6a6a6,
                            dashSize: 3,
                            gapSize: 1,
                            linewidth: 2
                        }
                    ), 
                    THREE.LinePieces
                );
            scene.add(enclosingCube);

            // renderer
            renderer = new THREE.WebGLRenderer(
                { 
                    antialias: true
                }
            );
            renderer.setClearColor(0x1d475f, 1.0);
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);

            container = document.getElementById( 'container' );
            container.appendChild( renderer.domElement );
            
            window.addEventListener('resize', onWindowResize, false );

            render();
            
            getAvatars();
            getMapTexture();
            getMapHeight();
            getMapTerrain();
        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize( window.innerWidth, window.innerHeight );
            //controls.handleResize();
            render();
        }

        function animate() {
            requestAnimationFrame(animate);
            controls.update();
        }

        function render() {
            renderer.render(scene,camera);
        }
        
        function enclosingCase(size) {
            var h = size * 0.5;
            var geometry = new THREE.Geometry();
            geometry.vertices.push(
                // Oz
                //new THREE.Vector3( -h, -h, -h ),
                //new THREE.Vector3( -h, h, -h ),

                new THREE.Vector3( -h, h, -h ),
                new THREE.Vector3( h, h, -h ),

                new THREE.Vector3( h, h, -h ),
                new THREE.Vector3( h, -h, -h ),
                
                // Ox
                //new THREE.Vector3( h, -h, -h ),
                //new THREE.Vector3( -h, -h, -h ),

                new THREE.Vector3( -h, -h, h ),
                new THREE.Vector3( -h, h, h ),

                new THREE.Vector3( -h, h, h ),
                new THREE.Vector3( h, h, h ),

                new THREE.Vector3( h, h, h ),
                new THREE.Vector3( h, -h, h ),

                new THREE.Vector3( h, -h, h ),
                new THREE.Vector3( -h, -h, h ),

                // Oy
                //new THREE.Vector3( -h, -h, -h ),
                //new THREE.Vector3( -h, -h, h ),

                new THREE.Vector3( -h, h, -h ),
                new THREE.Vector3( -h, h, h ),

                new THREE.Vector3( h, h, -h ),
                new THREE.Vector3( h, h, h ),

                new THREE.Vector3( h, -h, -h ),
                new THREE.Vector3( h, -h, h )
             );
            return geometry;
        }
        
        function makeTextSprite(message, parameters) {
            if ( parameters === undefined ) parameters = {};

            var fontface = parameters.hasOwnProperty("fontface") ? 
                parameters["fontface"] : 
                "Helvetiker";

            var fontsize = parameters.hasOwnProperty("fontsize") ? 
                parameters["fontsize"] : 
                18;

            var borderThickness = 
                parameters.hasOwnProperty("borderThickness") ? 
                parameters["borderThickness"] : 
                4;

            var borderColor = 
                parameters.hasOwnProperty("borderColor") ? 
                parameters["borderColor"] : 
                { r:0, g:0, b:0, a:1.0 };

            var backgroundColor = 
                parameters.hasOwnProperty("backgroundColor") ?
                parameters["backgroundColor"] : 
                { r:255, g:255, b:255, a:1.0 };
                
            var canvas = document.createElement('canvas');
            canvas.width = 162;
            canvas.height = 64;
            var context = canvas.getContext('2d');
            context.font = "Bold " + fontsize + "px " + fontface;
            context.imageSmoothingEnabled = true;

            // determine the longest line
            var textLines = message.split("\n");
            var longestLine = textLines[0].length;
            var longestIndex = 0;
            for(var i = 1; i <textLines.length; ++i) {
                if(textLines[i].length > longestLine) {
                    longestLine = textLines[i].length;
                    longestIndex = i;
                }
            }

            // get size data (height depends only on font size)
            var metrics = context.measureText(textLines[longestIndex]);
            var textWidth = metrics.width;

            // background color
            context.fillStyle = 
                "rgba(" + 
                    backgroundColor.r + 
                    "," + 
                    backgroundColor.g + 
                    "," + 
                    backgroundColor.b + 
                    "," + 
                    backgroundColor.a + 
                ")";
            // border color
            context.strokeStyle = 
                "rgba(" + 
                    borderColor.r + 
                    "," + 
                    borderColor.g + 
                    "," + 
                    borderColor.b + 
                    "," + 
                    borderColor.a + 
                ")"; 

            context.lineWidth = borderThickness;
            roundRect(
                context, 
                borderThickness/2, 
                borderThickness/2, 
                textWidth + borderThickness,
                // 1.2 is extra height factor for text below baseline
                textLines.length * fontsize * 1.2 + borderThickness,
                6
            );

            // add the text color
            context.fillStyle = 
                "rgba(" + 
                    borderColor.r + 
                    "," + 
                    borderColor.g + 
                    "," + 
                    borderColor.b + 
                    "," + 
                    borderColor.a + 
                ")";
            
            // add all the requested lines whist increasing lines
            for(var i = 0; i< textLines.length; ++ i) {
                context.fillText(
                    textLines[i], 
                    borderThickness,
                    fontsize
                );
                fontsize += fontsize;
            }

            // canvas contents will be used for a texture
            var spriteTexture = new THREE.Texture(canvas,
                THREE.UVMapping, // mapping
                THREE.ClampToEdgeWrapping, // wrapS
                THREE.ClampToEdgeWrapping, // wrapT
                THREE.NearestFilter, // magfilter
                THREE.NearestFilter, //minfilter,
                THREE.RGBAFormat, // format
                THREE.UnsignedByteType // type
            ) 
            spriteTexture.anisotropy = renderer.getMaxAnisotropy();
            spriteTexture.needsUpdate = true;
            var spriteMaterial = new THREE.SpriteMaterial( 
                {
                    map: spriteTexture, 
                    useScreenCoordinates: false,
                    overdraw: false, 
                    side:THREE.DoubleSide
                }
            );
            var sprite = new THREE.Sprite(spriteMaterial);
            sprite.scale.set(64,32, 1.0);
            return sprite;  
        }

        // function for drawing rounded rectangles
        function roundRect(ctx, x, y, w, h, r) 
        {
            ctx.beginPath();
            ctx.moveTo(x+r, y);
            ctx.lineTo(x+w-r, y);
            ctx.quadraticCurveTo(x+w, y, x+w, y+r);
            ctx.lineTo(x+w, y+h-r);
            ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
            ctx.lineTo(x+r, y+h);
            ctx.quadraticCurveTo(x, y+h, x, y+h-r);
            ctx.lineTo(x, y+r);
            ctx.quadraticCurveTo(x, y, x+r, y);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();   
        }

        /*************************************************************************/
        /*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
        /*************************************************************************/
        function wasMapValueToRange(value, xMin, xMax, yMin, yMax) {
            return yMin + (
                ( yMax - yMin ) * ( value - xMin ) / ( xMax - xMin )
            );
        }
        
        function updatePoints() {
            /* Remove points. */
            scatterPlot.remove(points);
            /* Remove labels. */
            for(i=0; i                scatterPlot.remove(avatarLabels[i]);
            }
            /* Remove projections. */
            for(i=0; i                scatterPlot.remove(avatarProjections[i])
            }
            
            var pointGeo = new THREE.Geometry();
            avatarLabels = [];
            avatarProjections = [];

            for (i=0; i            {
                for (j=2; j                {
                    /* For SecondLife the z and y axes must be swapped
                     * and then the x and y axes in order to attain
                     * the proper orientation for threejs.
                     */
                    var x = wasMapValueToRange(
                        csv.positions[i][1],
                        dataBounds.minx,
                        dataBounds.maxx,
                        plotBounds.minx/2,
                        plotBounds.maxx/2
                    );
                    var y = wasMapValueToRange(
                        csv.positions[i][j],
                        dataBounds.minz,
                        dataBounds.maxz,
                        plotBounds.minz/2,
                        plotBounds.maxz/2
                    );
                    var z = wasMapValueToRange(
                        csv.positions[i][0],
                        dataBounds.miny,
                        dataBounds.maxy,
                        plotBounds.miny/2,
                        plotBounds.maxy/2
                    );
                    
                    // Add the points
                    pointGeo.vertices.push(
                        new THREE.Vector3(
                            x, 
                            y + dotSize/4, 
                            z
                        ) 
                    );
                    
                    // Add the color for the point.
                    var pointColor = new THREE.Color().setRGB(
                        csv.colors[i][0], 
                        csv.colors[i][1], 
                        csv.colors[i][2]
                    );
                    pointGeo.colors.push(pointColor);
                    
                    // Add the projection in plane xOy
                    var lineMaterial = new THREE.LineDashedMaterial(
                        {
                           color: pointColor.getHex(),
                           linewidth: 1,
                           gapSize: 1
                        }
                    );
                    var lineGeometry = new THREE.Geometry();
                    lineGeometry.vertices.push(
                        new THREE.Vector3(
                            x, 
                            y, 
                            z
                        )
                    );
                    lineGeometry.vertices.push(
                        new THREE.Vector3(
                            x, 
                            plotBounds.minz/2, 
                            z
                        )
                    );
                    lineGeometry.computeLineDistances();
                    var xOyProjection = new THREE.Line(
                        lineGeometry, 
                        lineMaterial,
                        THREE.LineStrip
                    );
                    avatarProjections.push(xOyProjection)
                    scatterPlot.add(xOyProjection);
                    
                    // Generate the label.
                    var sprite = makeTextSprite(
                        " " + 
                        csv.names[i] + 
                        " " + 
                        "\n" +
                        " " + 
                        "@" + 
                        "<" + 
                        csv.positions[i][0] + 
                        "," + 
                        csv.positions[i][1] + 
                        "," + 
                        csv.positions[i][j] + 
                        ">" +
                        " ", 
                        { 
                            "fontsize": 9, 
                            "backgroundColor": 
                                {r:255, g:255, b:255, a:1}, 
                            "borderColor": 
                                {
                                    r:Math.round(wasMapValueToRange(
                                        csv.colors[i][0], 
                                        0, 
                                        1, 
                                        0, 
                                        255
                                    )), 
                                    g:Math.round(wasMapValueToRange(
                                        csv.colors[i][1], 
                                        0, 
                                        1, 
                                        0, 
                                        255
                                    )), 
                                    b:Math.round(wasMapValueToRange(
                                        csv.colors[i][2], 
                                        0, 
                                        1, 
                                        0, 
                                        255
                                    )),
                                    a:1
                                },
                            "borderThickness": 1 
                        } 
                    );
                    
                    sprite.position.x = pointGeo.vertices[i].x;
                    sprite.position.y = pointGeo.vertices[i].y;
                    sprite.position.z = pointGeo.vertices[i].z;
                    
                    avatarLabels.push(sprite);
                    scatterPlot.add(sprite);
                    
                }
            }
            var pointCloudTexture = THREE.ImageUtils.loadTexture( 
                "images/triangle.gif"
            );
            pointCloudTexture.anisotropy = renderer.getMaxAnisotropy();
            var pointCloudMaterial = new THREE.PointCloudMaterial(
            {
                  size: dotSize,
                  map: pointCloudTexture,
                  blending: THREE.NormalBlending, // required
                  depthTest: true, // required
                  transparent: true,
                  opacity: 1,
                  vertexColors: true // optional
            });
            //pointCloudTexture.needsUpdate = true;
            points = new THREE.PointCloud(
                pointGeo, 
                pointCloudMaterial
            );
            scatterPlot.add(points);
            scatterPlot.needsUpdate = true;
        }
        
        function updateMap() {
            if(mapBumpData == null || mapImageData == null) return;
            
            scatterPlot.remove(mapMesh);
            // texture used to generate "bumpiness"
            var bumpTexture = new THREE.ImageUtils.loadTexture(
                'data:image/png;base64,' + mapBumpData
            );
            bumpTexture.wrapS = 
                bumpTexture.wrapT = 
                THREE.RepeatWrapping;
            bumpTexture.anisotropy = renderer.getMaxAnisotropy();
            // magnitude of normal displacement
            var bumpScale = mapHeight;

            var mapTexture = new THREE.ImageUtils.loadTexture(
                'data:image/png;base64,' + mapImageData
            );
            mapTexture.anisotropy = renderer.getMaxAnisotropy();
            mapTexture.wrapS = mapTexture.wrapT;

            // Create the uniforms.
            this.customUniforms = {
                bumpTexture: { 
                    type: "t", 
                    value: bumpTexture 
                },
                bumpScale: { 
                    type: "f", 
                    value: bumpScale
                },
                mapTexture: { 
                    type: "t", 
                    value: mapTexture 
                },
            };

            // create custom material from the shader code above
            // that is within specially labelled script tags
            var customMaterial = 
                new THREE.ShaderMaterial( 
                {
                    uniforms: customUniforms,
                    vertexShader: document.getElementById(
                        'terrainVertex'
                    ).textContent,
                    fragmentShader: document.getElementById(
                        'terrainFragment'
                    ).textContent,
                    side: THREE.DoubleSide
                }
            );

            var planeGeo = new THREE.PlaneBufferGeometry(
                plotBounds.maxx, 
                plotBounds.maxy, 
                100,
                100
            );

            mapMesh = new THREE.Mesh(
                planeGeo,
                customMaterial
            );
            mapMesh.rotation.x = - Math.PI / 2;
            mapMesh.rotation.z = - Math.PI / 2;
            mapMesh.position.y = -plotBounds.maxy/2;
            scatterPlot.add(mapMesh);
            scatterPlot.needsUpdate = true;
        }
        
        function getAvatars() {
            $.ajax({
                /*type: 'POST',
                url: "getAvatars.php",
                cache: false,
                timeout: 60000*/
                type: 'POST',
                url: '/',
                data: {
                    command: 'getavatarpositions',
                    entity: 'region'
                },
                dataType: 'json'
            }).done(function(response) {
                /*switch(data != null && data != "") {
                    case false:
                        setTimeout(getAvatars, 1000);
                        return;
                }
                var json = jQuery.parseJSON(data);
                switch(JSON.stringify(json) != JSON.stringify(csv)) {
                    case true:
                        csv = json;
                        updatePoints();
                        render();
                        break;
                }
                setTimeout(getAvatars, avatarsFetchInterval);*/
                csv['names']
                    .concat($.stride(wasCSVToArray(response.data), 3));
                csv['positions']
                    .concat($.stride(wasCSVToArray(response.data).slice(2), 3));
                csv['colors']
                    .concat([255, 0, 0]);
            }).error(function() {
                setTimeout(getAvatars, 1000);
            });
        }
        
        function getMapTerrain() {
            $.ajax({
                type: 'POST',
                url: "generateTerrainHeightMap.php",
                cache: false,
                timeout: 60000
            }).done(function(data) {
                // Only update if the data changed.
                switch(data != null && data != '') {
                    case false:
                        setTimeout(getMapTerrain, 1000);
                        return;
                }
                switch(data != mapHeight) {
                    case true:
                        mapBumpData = data;
                        updateMap();
                        render();
                        break;
                }
                setTimeout(getMapTerrain, mapFetchInterval);
            }).error(function() {
                setTimeout(getMapTerrain, 1000);
            });
        }
        
        function getMapTexture() {
            $.ajax({
                type: 'POST',
                url: "getMapTexture.php",
                cache: false,
                timeout: 60000
            }).done(function(data) {
                // Only update if the data changed.
                switch(data != null && data != '') {
                    case false:
                        setTimeout(getMapTexture, 1000);
                        return;
                }
                switch(data != mapHeight) {
                    case true:
                        mapImageData = data;
                        updateMap();
                        render();
                        break;
                }
                setTimeout(getMapTexture, mapFetchInterval);
            }).error(function() {
                setTimeout(getMapTexture, 1000);
            });
        }
        
        function getMapHeight() {
            $.ajax({
                type: 'POST',
                url: "getMapHeight.php",
                cache: false,
                timeout: 60000
            }).done(function(data) {
                // Only update if the data changed.
                switch(data != null && data != '') {
                    case false:
                        setTimeout(getMapHeight, 1000);
                        return;
                }
                switch(data != mapHeight) {
                    case true:
                        mapHeight = data;
                        updateMap();
                        render();
                        break;
                }
                setTimeout(getMapHeight, mapFetchInterval);
            }).error(function() {
                setTimeout(getMapHeight, 1000);
            });
        }

    </script>
</div>

Generated by GNU Enscript 1.6.5.90.