API Docs for: 0.1.0
Show:

File: source/js/app/controls__gui.js

/**
 * @module ControlsGUI
 */
define(['text!controls__gui_info.txt'], function (instructions) {

    var triggerAnimation;

    /**
     * Creates the GUI.
     * @method createGUI
     * @param  {object} planetShortcuts    Array of planet names and the keyboard shortcut to use to snap to them.  
     * @param  {Function} triggerAnimationParameter Provides a hook for updating animation after changing input values (@TODO - this is a code smell. Should decouple the animation from the GUI inputs.)
     */
    function createGUI(planetShortcuts, triggerAnimationParameter) {
        triggerAnimation = triggerAnimationParameter;
        
        var canvasContainer       = document.getElementById('canvas_solar_system__container'),
            instructionsContainer = document.createElement('DIV'),
            guiContainer          = document.createElement('DIV');
        
        canvasContainer.insertBefore(instructionsContainer, canvasContainer.firstChild);
        canvasContainer.appendChild(guiContainer);
        guiContainer.id = 'webgl_solarsystem_gui';
        instructionsContainer.id = 'webgl_solarsystem_instructions';

        createInstructions(instructionsContainer, planetShortcuts);
        createSliders(guiContainer);
    }

    /**
     * @method createInstructions
     * @param  {DOMElement} instructionsContainer  The DIV we need to populate with instructions.
     * @param  {object} planetShortcuts    Array of planet names and the keyboard shortcut to use to snap to them. 
     */
    function createInstructions(instructionsContainer, planetShortcuts) {
        instructionsContainer.innerHTML = instructions;
        for (var shortcut in planetShortcuts) {
            instructionsContainer.innerHTML += '<strong>' + shortcut + '</strong>: ' + planetShortcuts[shortcut].name + ', ';
        }
    }

    /**
     * Creates a DIV and appends to the given DOMElement container.
     * 
     * @method createDiv
     * @param  {DOMElement} guiContainer The container to append the DIV to.
     * @param  {String}   classes        The classname to give the DIV.
     * @param  {Function} callback       Optional callback function.
     * @return {DOMElement}              The newly created DIV.
     */
    function createDiv(guiContainer, classes, callback) {
        var newDiv = document.createElement('DIV');
        newDiv.className = classes;
        guiContainer.appendChild(newDiv);

        if (callback) {
            callback();
        }

        return newDiv;
    }

    /**
     * Creates the sliders in the GUI.
     * @method createSliders
     * @param  {DOMElement} guiContainer Document element to insert the sliders in.
     */
    function createSliders(guiContainer) {
        var speedContainer               = createDiv(
                guiContainer,
                'webgl_solarsystem_gui__fieldset_container webgl_solarsystem_gui__fieldset_container--cols_2'
            ),
            shininessContainer           = createDiv(
                guiContainer,
                'webgl_solarsystem_gui__fieldset_container webgl_solarsystem_gui__fieldset_container--cols_2',
                function () {
                    var separator = document.createElement('HR');
                    separator.setAttribute('style', 'clear: both;');
                    guiContainer.appendChild(separator);
                }
            ),
            lightingContainerAmbient     = createDiv(
                guiContainer,
                'webgl_solarsystem_gui__fieldset_container webgl_solarsystem_gui__fieldset_container--cols_3'
            ),
            lightingContainerSunSpecular = createDiv(
                guiContainer,
                'webgl_solarsystem_gui__fieldset_container webgl_solarsystem_gui__fieldset_container--cols_3'
            ),
            lightingContainerSunDiffuse  = createDiv(
                guiContainer,
                'webgl_solarsystem_gui__fieldset_container webgl_solarsystem_gui__fieldset_container--cols_3'
            ),
            speedInfo;

        var globals = {
            ambient:   0.4,
            specular:  0.7,
            diffuse:   0.7,
            shininess: 5
        };

        createSlider({
            label:      'Time',
            id:         'millisecondsPerDay',
            minLabel:   'Fast',
            maxLabel:   'Slow',
            min:        10,
            max:        5000,
            step:       10,
            defaultVal: 1000,
            container:  speedContainer,
            onChangeCallback: updateMillisecondsPerDay
        });

        speedInfo = document.createElement('DIV');
        speedInfo.id = 'millisecondsPerDayInfo';
        speedContainer.appendChild(speedInfo);
        updateMillisecondsPerDay();

        createSlider({
            label:      'Planet shininess',
            id:         'planetShininess',
            min:        0,
            max:        255,
            step:       1,
            defaultVal: globals.shininess,
            container:  shininessContainer
        });

        createSlider({
            label:      'Ambient Light - Global',
            id:         'ambientGlobal',
            min:        0,
            max:        1,
            defaultVal: globals.ambient,
            step:       0.1,
            container:  lightingContainerAmbient,
            onChangeCallback: function () {
                updateValueOfSliders('ambientGlobal', ['ambientR', 'ambientG', 'ambientB']);
            }
        });

        createSlider({
            label:      'Ambient Light - Red',
            id:         'ambientR',
            min:        0,
            max:        1,
            defaultVal: globals.ambient,
            step:       0.1,
            container:  lightingContainerAmbient
        });

        createSlider({
            label:      'Ambient Light - Green',
            id:         'ambientG',
            min:        0,
            max:        1,
            defaultVal: globals.ambient,
            step:       0.1,
            container:  lightingContainerAmbient
        });

        createSlider({
            label:      'Ambient Light - Blue',
            id:         'ambientB',
            min:        0,
            max:        1,
            defaultVal: globals.ambient,
            step:       0.1,
            container:  lightingContainerAmbient
        });

        createSlider({
            label:      'Specular term - Global',
            id:         'pointGlobalSpecular',
            min:        0,
            max:        1,
            defaultVal: globals.specular,
            step:       0.1,
            container:  lightingContainerSunSpecular,
            onChangeCallback: function () {
                updateValueOfSliders('pointGlobalSpecular', ['pointRSpecular', 'pointGSpecular', 'pointBSpecular']);
            }
        });

        createSlider({
            label:      'Specular term - Red',
            id:         'pointRSpecular',
            min:        0,
            max:        1,
            defaultVal: globals.specular,
            step:       0.1,
            container:  lightingContainerSunSpecular
        });

        createSlider({
            label:      'Specular term - Green',
            id:         'pointGSpecular',
            min:        0,
            max:        1,
            defaultVal: globals.specular,
            step:       0.1,
            container:  lightingContainerSunSpecular
        });

        createSlider({
            label:      'Specular term - Blue',
            id:         'pointBSpecular',
            min:        0,
            max:        1,
            defaultVal: globals.specular,
            step:       0.1,
            container:  lightingContainerSunSpecular
        });

        createSlider({
            label:      'Diffuse term - Global',
            id:         'pointGlobalDiffuse',
            min:        0,
            max:        1,
            defaultVal: globals.diffuse,
            step:       0.1,
            container:  lightingContainerSunDiffuse,
            onChangeCallback: function () {
                updateValueOfSliders('pointGlobalDiffuse', ['pointRDiffuse', 'pointGDiffuse', 'pointBDiffuse']);
            }
        });

        createSlider({
            label:      'Diffuse term - Red',
            id:         'pointRDiffuse',
            min:        0,
            max:        1,
            defaultVal: globals.diffuse,
            step:       0.1,
            container:  lightingContainerSunDiffuse
        });

        createSlider({
            label:      'Diffuse term - Green',
            id:         'pointGDiffuse',
            min:        0,
            max:        1,
            defaultVal: globals.diffuse,
            step:       0.1,
            container:  lightingContainerSunDiffuse
        });

        createSlider({
            label:      'Diffuse term - Blue',
            id:         'pointBDiffuse',
            min:        0,
            max:        1,
            defaultVal: globals.diffuse,
            step:       0.1,
            container:  lightingContainerSunDiffuse
        });
    }

    /**
     * Creates a slider.
     * @method createSlider
     * @param  {Object} config Configuration object.
     */
    function createSlider(config) {
        var fieldset = document.createElement('FIELDSET'),
            slider   = document.createElement('INPUT'),
            label    = document.createElement('LABEL');
        
        label.innerHTML = config.label;

        slider.type  = 'range';
        slider.id    = config.id;
        slider.step  = config.step || 1;
        slider.min   = config.min;
        slider.max   = config.max;
        slider.value = config.defaultVal;

        if (config.onChangeCallback) {
            slider.oninput = function () {
                config.onChangeCallback();
                triggerAnimation();
            };
        }
        else {
            slider.oninput = triggerAnimation;
        }

        config.container.appendChild(fieldset);
        fieldset.appendChild(label);
        fieldset.appendChild(document.createTextNode(config.minLabel || slider.min));
        fieldset.appendChild(slider);
        fieldset.appendChild(document.createTextNode(config.maxLabel || slider.max));
    }

    /**
     * Updates the value displayed to users when they change the number of milliseconds per day.
     * @method updateMillisecondsPerDay
     */
    function updateMillisecondsPerDay() {
        var inputValue = document.getElementById('millisecondsPerDay').value,
            info  = document.getElementById('millisecondsPerDayInfo'),
            millisecondsPerDay = parseInt(inputValue, 10),
            secondsPerDay = millisecondsPerDay / 1000,
            html = '1 Earth day = ';

        if (secondsPerDay < 1) {
            html += millisecondsPerDay + ' milliseconds';
        }
        else {
            html += (secondsPerDay + ' ') + (secondsPerDay > 1 ? 'seconds' : 'second');
        }

        info.innerHTML = html;
    }

    /**
     * Updates the value of all the given sliders. This way we can have one 'master' slider that controls all the others.
     * @method updateValueOfSliders
     * @param  {String} globalSlider    ID of the master slider.
     * @param  {array} slidersToUpdate Array of IDs of the sliders that should be updated when the master is updated.
     */
    function updateValueOfSliders(globalSlider, slidersToUpdate) {
        var value = document.getElementById(globalSlider).value,
            currentSlider;

        for (var i = 0; i < slidersToUpdate.length; i++) {
            currentSlider = document.getElementById(slidersToUpdate[i]);
            currentSlider.value = value;
        }
    }

    /**
     * @class ControlsGUI
     */
    return {
        /**
         * Initialises the GUI.
         * @method init
         * @constructor
         */
        init: createGUI
    };
});