<file_sep>app.controller('planecon', function($scope, contFact) {
    $scope.m = {
        x: 0,
        y: 0
    }
    $scope.elev = 0;
    $scope.rud = 0;
    $scope.leftAil = 0;
    $scope.rightAil = 0;
    $scope.groundDisp = {
        x: 0,
        y: 0
    };
    $scope.worldRot = {
        x: 0,
        y: 0,
        z: 0
    };
    $scope.throt = 0;
    $scope.propRot = 0;
    $scope.user = Math.floor(Math.random() * 999999999).toString(32); //randomly chosen username

    $scope.moveMode = true;
    //cylinder for the prop
    contFact.cylMaker(15, 15, 5, '#prop-cyl-anchor', { x: 2, y: 2, z: -10 }, { x: 90, y: 0, z: 0 }, false, { h: 0, s: 0, v: 20 })
        //right leg
    contFact.cylMaker(15, 60, 7, '.fuse-panel:nth-of-type(1)', { x: 30, y: 0, z: -15 }, { x: 0, y: 30, z: 70 }, false);
    contFact.cylMaker(15, 60, 7, '.fuse-panel:nth-of-type(1)', { x: 30, y: 60, z: -15 }, { x: 0, y: 30, z: 110 }, false);
    //left leg
    contFact.cylMaker(15, 60, 7, '.fuse-panel:nth-of-type(2)', { x: 70, y: 0, z: -15 }, { x: 0, y: 150, z: 70 }, false);
    contFact.cylMaker(15, 60, 7, '.fuse-panel:nth-of-type(2)', { x: 70, y: 60, z: -15 }, { x: 0, y: 150, z: 110 }, false);

    //right wheel
    contFact.cylMaker(15, 7, 45, '#whl-right', { x: 0, y: 0, z: 25 }, { x: 90, y: 0, z: 0 }, true, { h: 0, s: 0, v: 20 });

    //left wheel
    var testMode = false;
    contFact.cylMaker(15, 7, 45, '#whl-left', { x: 0, y: 0, z: 25 }, { x: 90, y: 0, z: 0 }, true, { h: 0, s: 0, v: 20 });
    window.onmousemove = function(e) {
        if ($scope.moveMode && (!$scope.phoneId || testMode)) {
            $scope.elev = 60 * (((e.y || e.clientY) / $(document).height()) - .5);
            $scope.rud = -60 * (((e.x || e.clientX) / $(document).width()) - .5);
            $scope.leftAil = 30 * (((e.x || e.clientX) / $(document).width()) - .5);
            $scope.rightAil = -30 * (((e.x || e.clientX) / $(document).width()) - .5);
            $scope.worldRot.z = e.x || e.clientX;
            $scope.worldRot.x = e.y || e.clientY;
        } else if (!testMode) {
            $scope.throt = 200 * (1 - ((e.y || e.clientY) / $(document).height()));
        }
        $scope.$digest();
    }
    window.onkeydown = function(e) {
        if (e.which == 83) {
            $scope.moveMode = !$scope.moveMode;
        }
        //q:81, a:65
        else if (e.which == 81 && $scope.throt < 198) {
            $scope.throt += 2;
        } else if (e.which = 65 && $scope.throt > 2) {
            $scope.throt -= 2;
        }
    }
    var engine = setInterval(function() {
        //main timer
        $scope.propRot += $scope.throt;
        $scope.propRot = $scope.propRot % 360; //prevent overflow!
        $scope.groundDisp.y -= $scope.throt / 10;
        if (!testMode) {
            $scope.handleSurfaces();
        }
        $scope.$digest();
    }, 40)
    $scope.explCode = function() {
        bootbox.confirm({
            title: "Phone ID Code &#128241;",
            message: "The Phone ID Code &#128241; links one particular smartphone with one particular instance of this demo. Each phone code can only be used once. A new code can be obtained by visiting daveheligame.herokuapp.com on your phone. Your phone will be used as a joystick.<hr/>",
            buttons: {
                confirm: {
                    label: 'Okay',
                    className: 'btn-primary'
                }
            },
            callback: function(result) {
                console.log('This was logged in the callback: ' + result);
            }
        });
    };
    $scope.testCodeTimer = null;
    $scope.testPhone = function() {
        if ($scope.testCodeTimer) clearInterval($scope.testCodeTimer);
        $scope.testCodeTimer = setTimeout(function() {
            console.log('sending out', $scope.phoneIdCand);
            socket.emit('checkPhone', { code: $scope.phoneIdCand });
        }, 500)
    }
    socket.on('phoneCheckResult', function(r) {
        console.log(r);
        $scope.phoneValid = r.valid;
        $scope.$digest();
    })
    $scope.submitCode = function() {
        socket.emit('registerPhones', { joy: $scope.phoneIdCand, desk: $scope.user })
        $scope.phoneId = $scope.phoneIdCand;
        if ($scope.phoneIdCand == 'dave123') {
            testMode = true;
        } else {
            $scope.worldRot = {
                x: 0,
                y: 0,
                z: 0
            }
        }
        document.querySelector('#plane-main').style.transform = 'translateZ(-130px) translateY(40px) rotateX(90deg) rotateY(180deg)'
    };
    $scope.handleSurfaces = function() {
        //surfaces range from 0-35 in either direction
        $scope.worldRot.x -= ($scope.elev / 35) * ($scope.throt / 150);
        $scope.worldRot.y -= ($scope.rud / 35) * ($scope.throt / 150);
        $scope.worldRot.z += ($scope.rightAil / 35) * ($scope.throt / 150);
        $scope.$digest();
    };
    socket.on('oriToDesk', function(ori) {
        if (ori.u == $scope.user) {
            //ori cmd is for this instance;
            if (ori.r == 'joy') {
                $scope.elev = (.5 * ori.x);
                $scope.rud = 0 - (.5 * ori.z);
                $scope.leftAil = 0 - (.5 * ori.y);
                $scope.rightAil = (.5 * ori.y);
                $scope.$digest();
            }

            $scope.$apply();
        }
    });
})
<file_sep>#Dave's Heli Sim &#128641;

##Contents: 
 - [&#128712;About](#about)
 - [&#128377;Usage](#usage)
 - [&#128421;Server](#server)
 - [&#128202;Technical Stuff](#technical-stuff)
 - [&#128203;Credits](#credits)

#About
Dave's Heli Sim is a simple demonstration of the power of raw CSS/JS. I'm using css and some relatively basic trigonometry/physics to animate (and then allow you to fly) a helicopter. I use AngularJS for most of the animations.

##Usage
###Setup
To use Dave's Heli Sim, you'll need follow the following instructions:

*NOTE: The server's not live yet, so for now you'll just have to wait and see!*

 1. Log onto the site (*pending!* Something on Herokuapp.com). 
 2. Decide how you want to control the heli. You can either use two phones or one phone and your mouse.
 3. For each phone, log onto the site. You'll be redirected to the mobile version of the site.
 4. Again, for each phone, enter the code displayed on the phone under Phone Code in the box on the desktop version of the site.
 5. Click "Done" on the desktop version.
 6. Fly!

###Controls (phone&#128241;)

 - The **cyclic** is akin to the joystick in other aircraft: it controls, generally, speaking, the direction of the vehicle. Note that the initial position of your phone is considered the "neutral" point. Remember that **pitch** is forward and back tilt, **yaw** is left and right in a flat plane, while **roll** is left and right about an axis from nose to tail. When going around a corner, your car *yaws*, but it does not (hopefully!) *roll*.
  - Roll left to increase lift on the right-facing blades, while decreasing it on the left-facing blades, thus pushing the aircraft left.
  - Roll right to move to the right.
  - Yaw left to simulate pushing the left pedal. This temporarily decreases the blade angle of the tail rotors. In turn, this increases the effect of the torque of the main rotors, thus turning the aircraft left.
  - Yaw right to turn right.
  - Pitch forward to increase the angle of the rear-facing blades, while decreasing the angle of the forward facing blade. This pushes the aircraft forward.
  - Pitch backwards to push the aircraft backwards. 
 - The **collective** is generally the 'power' control. However, in most helicopters, the blades actually spin at a constant rate. Instead, it's the job of the collective to adjust the angle of the blades. 
  - Tilt backwards to decrease blade angle, and thus decrease lift.
  - Tilt forwards to increase blade angle, and thus increase lift.

###Controls (mouse&#128432;)
*NOTE: if you're using a one button mouse, you will not be able to yaw your craft. Sorry!*

 - **Cyclic**
  - Move the mouse to the left temporarily to slide left.
  - Move the mouse to the right to slide right.
  - Move the mouse to forward to pitch forward and begin moving forward.
  - Move the mouse backwards to pitch back, and begin sliding backwards.
  - Finally, click the left and right mousebuttons to yaw left or right (i.e., use the rudder pedals);
 - **Collective**
  - Move the mouse forward to increase throttle. 
  - Move the mouse backwards to decrease throttle.

##Server
Currently, there is no live version of this app. However, it'll eventually be located (probably!) at *daveheligame.herokuapp.com*.

##Technical Stuff
Dave's Heli Game uses the following technologies:

 - AngularJS for front-end responsiveness. Particularly, I'm using it to animate the different parts of the heli.
 - The native deviceorientation API (`window.addeventlistener("deviceorientation",callback)`) to capture phone orientation information.
 - NodeJS and ExpressJS on the back-end for the server.
 - Websockets, via socket.io, to allow quick communication between the server, phones, and game.
 - CSS3 for the 3d. I'm also using some of CSS3's filters (namely the `blur()` filter) to create a motion blur effect.
 - Lots and lots of trigonometry and basic physics to control the aircraft's movement. I am *not* an aircraft person, so I'm only using what I can google! If it looks wrong, it probably is!

##Credits
Dave's Heli Game was written by me, [Dave](https://github.com/Newms34). Other various libraries/technologies are from their respective creators. I'm just usin em.<file_sep>app.factory('movFact', function($rootScope) {
    var mouseStatus = [0, 0];
    return {
        handlePhoneCyc: function(o) {
            var oriObj = {
                FB: 30 * (o.x + 70) / 140,
                RL: 30 * (o.y + 70) / 140,
                pedals: 30 * (o.z + 70) / 140
            };
            return oriObj;
        },
        handlePhoneCol: function(o) {
            return 30 * (o.x + 70) / 140;
        },
        handleMouseOri: function(x, y, w, h, z) {
            var oriObj = {
                FB: -15 * (y - z.y) / (h / 2),
                RL: 15 * (x - z.x) / (w / 2)
            };
            return oriObj;
        },
        handleMouseThrot: function(y, h, z) {
            return 15 + (30 * (z.y - y) / (h));
        },
        handleMousePedal: function(c, m) {
            var ang = 0;
            if (m && m == 1) {
                //mousedown
                //first, we deal with the case where both buttons are down:
                if (c.button === 0 && mouseStatus[1]) {
                    mouseStatus[0] = 1;
                    ang = 0;
                } else if (c.button == 2 && mouseStatus[0]) {
                    mouseStatus[1] = 1;
                    ang = 0;
                } else if (c.button === 0) {
                    mouseStatus[0] = 1;
                    ang = -10;
                } else {
                    mouseStatus[1] = 1;
                    ang = 10;
                }
            } else if (c.button === 0 && mouseStatus[1] == 1) {
                mouseStatus[0] = 0;
                ang = 10;
            } else if (c.button == 2 && mouseStatus[0] == 1) {
                mouseStatus[1] = 0;
                ang = -10;
            } else {
                //no buttons currently down
                mouseStatus = [0, 0];
                return 0;
            }
            return ang;
        }
    };
});
