<commit_before>/*

#DSV:0.5#

Collision Response - http://elancev.name/oliver/2D%20polygon.htm

*/


(function(window){

    'use strict';

    var $e = new function(){

        var constants = {
            //g: 6674*Math.pow(10,-11)
            g: 0.01
        }

        var internal = {
            debug: true,
            started: false,
            canvas: "",
            ctx: "",
            size: 0,
            time: {
                FPS: 1,
                FPScount: 0,
                FPSsum: 0,
                lossedFrames: 0,
                maxFPS: 60,
                minFPS: 20,
                deltaTime: 1,
                startTime: 0,
                behindTime: 0,
                elapsedTime: 0,
                miliseconds: 0,
                speed: 1, //Multiplier
                catchUpTime: 1/8, //S
                intervalClear: 10000, //MS
                elapsedLastClear: 0, //MS
                intervalRestartDelay: 40 //MS
            },
            controlVars: {
                update: {
                    finish: false,
                    exceeded: 0,
                    alert: 50,
                    alerted: false
                },
                start: false
            },
            debugVars: [],
            console: {
                history: [],
                border: false,
                font: '7pt Calibri',
                color: 'black'
            },
            world: {
                gravity: 10,
                drag: 1
            },
            globals: {
                maxForce: 150,
                opacity: 0.9,
                background: "#FFF"
            },
            mouse: 0,
            mouseover: undefined,
            layers: [],
            phycs: [],
            user: {}
        }

        //Public Functions start

        this.init = function(canvas, start, update){
            if(!internal.started){
                if(canvas != undefined && typeof canvas == "object"){
                    if(typeof start == "function" && typeof update == "function"){
                        try {
                            internal.canvas = canvas;
                            internal.ctx = canvas.getContext("2d");
                            internal.size = new this.Vector2(0,0);
                            internal.size.x = canvas.width;
                            internal.size.y = canvas.height;
                            internal.user.start = start;
                            internal.user.udpate = update;
                            Start();
                            internal.started = true;
                        } catch (e) {
                            $d.LogError("The element is not a canvas");
                        }
                    } else {
                        $d.LogError("Missing Start/Update functions");
                    }
                } else {
                    $d.LogError("Invalid element");
                }
            } else {
                $d.LogWarning("The engine is already running!");
            }
        }

        this.setMaxFPS = function(value){
            if(typeof value == "number"){
                internal.time.maxFPS = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.setDebug = function(value){
            if(typeof value == "boolean"){
                internal.debug = value;
            } else {
                $d.LogError("Invalid value, expected boolean");
            }
        }

        this.setSpeed = function(value){
            if(typeof value == "number"){
                internal.time.speed = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.setGravity = function(value){
            if(typeof value == "number"){
                internal.world.gravity = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.stats = function(){
            $d.Log("STATS");
            $d.Log("FPS: " + internal.time.FPS);
            $d.Log("Current state: " + ((internal.started)? "Running" : "Stopped"));
            $d.Log("Frame loss count: " + internal.time.lossedFrames);
            $d.Log("Elapsed Time: " + $d.FormatMiliseconds(internal.time.elapsedTime));
            $d.Stats();
        }

        this.add2DObject = function(obj, layer){
            var ind = internal.phycs.push(obj);
            if(internal.layers[layer+50] == undefined){
                internal.layers[layer+50] = [];
            }
            internal.layers[layer+50].push(internal.phycs[ind-1]);
        }

        this.addDebugObject = function(name, obj, vars, duration){
            internal.debugVars.push({
                name: name,
                obj: obj,
                vars: vars,
                duration: duration
            });
        }

        this.writeDebugConsole = function(string, type){
            internal.console.history.push({name: string, type: type});
        }

        //close

        //Private functions start

        function Start(){
            internal.controlVars.update.finish = true;
            var sd = new Date().getTime();
            internal.time.miliseconds = sd;
            internal.mouse = new $e.Vector2(0,0);

            //Creates a new console
            internal.console.size = new $e.Vector2(50,50);
            internal.console.position = new $e.Vector2(internal.size.x - internal.console.size.x,0);

            //Creates the object for the mouse position
            internal.mouse = new $e.Object2D("Mouse", 0, 0, 1, 1, 1);
            internal.mouse.setCollider(new $e.BoxCollider(0.1,0.1));

            internal.canvas.addEventListener('mousemove', function(evt) {
                UpdateMousePos(internal.canvas, evt);
            }, false);

            internal.time.interval = StartInterval();
        }

        function StartInterval(){
            return setInterval(function(){
                if(internal.controlVars.update.finish){
                    internal.controlVars.update.finish = false;
                    internal.controlVars.update.alerted = false;
                    internal.controlVars.update.exceeded = 0;

                    var d = new Date().getTime();
                    var t = d - internal.time.miliseconds;
                    internal.time.elapsedTime += t;
                    internal.time.elapsedLastClear += t;
                    internal.time.deltaTime = (t/1000) * internal.time.speed;
                    internal.time.miliseconds = d;
                    internal.time.FPS = 1/(internal.time.deltaTime/internal.time.speed);
                    internal.time.FPSsum += internal.time.FPS;
                    internal.time.FPScount++;
                    if(internal.time.FPS > 0 && internal.time.FPS < internal.time.minFPS){
                        $d.LogWarning("Running behind the main timeline ("+(internal.time.FPS+internal.time.behindTime)+" seconds) trying to catch up at " + internal.time.catchUpTime + " seconds per frame.");
                        internal.time.behindTime += internal.time.deltaTime;
                        internal.time.deltaTime = 1/internal.time.minFPS;
                    } else {
                        if(internal.time.behindTime > 0){
                            if(internal.time.behindTime < internal.time.catchUpTime){
                                internal.time.deltaTime += internal.time.behindTime;
                                internal.time.behindTime = 0;
                                $d.LogWarning("Main timeline reached.");
                            } else {
                                internal.time.deltaTime += internal.time.catchUpTime;
                                internal.time.behindTime -= internal.time.catchUpTime;
                            }
                        }

                    }
                    if(internal.time.elapsedLastClear >= internal.time.intervalClear){
                        internal.time.elapsedLastClear = 0;
                        clearInterval(internal.time.interval);
                        ReStartInterval();
                    } else {
                        Update();
                    }
                } else {
                    internal.controlVars.update.exceeded++;
                    internal.time.lossedFrames++;
                    if(internal.controlVars.update.exceeded >= internal.controlVars.update.alert){
                        $d.LogWarning("Loosing frames, consider using a lower maxFPS("+internal.time.FPS+") value or reivew your code.");
                        internal.controlVars.update.alerted = true;
                    }
                }
            }, 1000/internal.time.maxFPS);
        }

        function ReStartInterval(){
            setTimeout(function(){
                internal.controlVars.update.finish = true;
                internal.time.interval = StartInterval();
            }, internal.time.intervalRestartDelay);
        }

        function Update(){
            try{
                internal.user.udpate();    
            } catch(e){
                $d.LogError("Error in User Update function", e);
            }
            UpdatePhysics();
            DrawObjects();
            DrawFPS();
            DrawMousePosition();
            DrawDebug();
            DrawConsole();
            internal.controlVars.update.finish = true;
        }

        function DrawFPS(){
            internal.ctx.font = "12px Arial";
            internal.ctx.fillStyle = "#000";
            //internal.ctx.fillText("FPS: " + Math.ceil(internal.time.FPSsum / internal.time.FPScount),10,10);
            internal.ctx.fillText("FPS: " + Math.round(internal.time.FPS),10,10);
        }

        function DrawMousePosition(){
            var msg = 'Mouse position: ' + internal.mouse.pos.x + ',' + internal.mouse.pos.y;
            internal.ctx.font = '8pt Calibri';
            internal.ctx.fillStyle = 'black';
            internal.ctx.fillText(msg, 10, 25);
        }

        function DrawConsole(){

        }

        function DrawDebug(){
            var offset = 0;
            for(var i = internal.debugVars.length - 1; i >= 0; i--){
                if(internal.debugVars[i].duration == undefined || internal.debugVars[i].duration > 0){
                    var finalObj = internal.debugVars[i].obj;
                    for(var x = 0; x < internal.debugVars[i].vars.length; x++){
                        finalObj = finalObj[internal.debugVars[i].vars[x]];
                    }
                    var msg = internal.debugVars[i].name + ":\t" + finalObj.toString(2);
                    internal.ctx.font = '7pt Calibri';
                    internal.ctx.fillStyle = 'black';
                    internal.ctx.fillText(msg, 10, internal.size.y - 10*(internal.debugVars.length - i - offset));
                    if(internal.debugVars[i].duration != undefined){
                        internal.debugVars[i].duration--;
                    }
                } else {
                    offset++;
                }
            }
        }

        function UpdateMousePos(canvas, evt) {
            var rect = canvas.getBoundingClientRect();
            internal.mouse.pos.x = evt.clientX - rect.left;
            internal.mouse.pos.y = internal.size.y - evt.clientY + rect.top;
            internal.mouseover = CheckCollision(internal.mouse, true);
        }

        function UpdatePhysics(){
            var objA;
            for(var i = 0; i < internal.phycs.length; i++){

                objA = internal.phycs[i];

                if(!objA.kinematic){
                    CheckCollision(objA, false);  
                    objA.collider.checked = internal.time.elapsedTime;

                    objA.rotation -= objA.angularVelocity * internal.time.deltaTime;

                    objA.velocity.x += objA.force.x/objA.mass;

                    objA.velocity.y -= internal.world.gravity;
                    objA.velocity.y += objA.force.y/objA.mass;

                    objA.pos = objA.pos.sum(objA.velocity.multiply(internal.time.deltaTime));
                    if(objA.newtonian){
                        for(var j = 0; j < internal.phycs.length; j++){
                            var objB = internal.phycs[j];
                            if(objB != objA){
                                if(objB.newtonian){
                                    var dist = objB.pos.substract(objA.pos);
                                    var dir = dist.normalized();
                                    dist = dist.magnitude();
                                    var f = constants.g*((objA.mass * objB.mass)/dist)
                                    dir.scale(f);
                                    objA.addForce(dir.x, dir.y);
                                }
                            }
                        }
                    }
                }
            }
        }

        function CheckCollision(obj, retObj){
            var objA = obj;
            var objB;
            var v3 = new $e.Vector2(0,0);
            for(var i = 0; i < internal.phycs.length; i++){
                objB = internal.phycs[i];
                if(objB.collider.checked != internal.time.elapsedTime || retObj){
                    if(objB != obj && (!objA.kinematic || !objB.kinematic)){
                        v3.x = objB.pos.x - objA.pos.x;
                        v3.y = objB.pos.y - objA.pos.y;
                        var dist = v3.magnitude();
                        dist = dist - objA.collider.maxRadius - objB.collider.maxRadius;
                        //$d.Log(dist);
                        if((dist) < 0){

                            var Na, Nb, sep;
                            var contactPA, contactPB;
                            var trueCollide = false;

                            //COLLISION CIRCLE-CIRCLE
                            if(objA.collider.type == 1 && objB.collider.type == 1){
                                if(retObj) return objB;
                                trueCollide = true;
                                //sep = objB.pos.substract(objA.pos);
                                var dir = objB.pos.substract(objA.pos).normalized();
                                sep = dir.multiply(objA.pos.substract(objB.pos).magnitude() - objA.collider.maxRadius - objB.collider.maxRadius);
                                Na = objA.pos.substract(objB.pos).normalized();
                                Nb = objB.pos.substract(objA.pos).normalized();
                            } else if(objA.collider.type == 1 && (objB.collider.type == 0 || objB.collider.type == 2)){ //COLLISION CIRCLE-POLYGON
                                var closest = {
                                    vertex: -1,
                                    distance: 1000000
                                };
                                /*for(var j = 0; j < objB.collider.vertexs.length; j++){
                                    var sub = objB.collider.vertexs[j].clone().rrotate(objB.rotation).sum(objB.pos).substract(objA.pos);
                                    var dist = sub.magnitude();
                                    var dir = sub.normalized();
                                    if(dist < objA.collider.radius){
                                        if(closest.distance > dist){
                                            closest.distance = dist;
                                            closest.vertex = j;
                                            closest.dir = dir;
                                        }
                                    }
                                }*/
                                if(closest.vertex == -1){
                                    for(var j = 0; j < objB.collider.vertexs.length; j++){
                                        var aV = objB.collider.vertexs[j].rrotate(objB.rotation).sum(objB.pos);
                                        var bV;
                                        var p = objA.pos;
                                        if(j == objB.collider.vertexs.length - 1){
                                            bV = objB.collider.vertexs[0].rrotate(objB.rotation).sum(objB.pos);
                                        } else {
                                            bV = objB.collider.vertexs[j+1].rrotate(objB.rotation).sum(objB.pos);
                                        }
                                        //var dist = (((aV.x - p.x)*(bV.y-p.y))-((aV.y-p.y)*(bV.x-p.x)))/(bV.substract(aV).magnitude());
                                        var resp = pnt2line(p, aV, bV);
                                        if(resp.dist < objA.collider.radius){
                                            if(closest.distance > resp.dist){
                                                closest.distance = resp.dist;
                                                closest.vertex = j;
                                                closest.dir = resp.nearest.substract(p).normalized();
                                                contactPB = resp.nearest.substract(objB.pos);
                                                contactPA = closest.dir.multiply(objA.collider.radius);
                                            }
                                        }
                                    }
                                }
                                if(closest.vertex != -1){
                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //CIRCLE
                                    sep = closest.dir.multiply(objB.pos.substract(objA.pos).magnitude() - objB.collider.maxRadius - objA.collider.maxRadius);
                                    Na = objA.pos.substract(objB.pos).normalized();

                                    //POLYGON
                                    MTDb = new $e.Vector2(0,0);
                                    Nb = objB.collider.normals[closest.vertex].clone();
                                    Nb.rotate(objB.rotation);
                                }
                            } else if((objA.collider.type == 0 || objA.collider.type == 2) && objB.collider.type == 1){ //COLLISION POLYGON-CIRCLE
                                var closest = {
                                    vertex: -1,
                                    distance: 1000000
                                };
                                /*for(var j = 0; j < objA.collider.vertexs.length; j++){
                                    var sub = objA.collider.vertexs[j].clone().rrotate(objA.rotation).sum(objA.pos).substract(objB.pos);
                                    var dist = sub.magnitude();
                                    var dir = sub.normalized();
                                    if(dist < objB.collider.radius){
                                        if(closest.distance > dist){
                                            closest.distance = dist;
                                            closest.vertex = j;
                                            closest.dir = dir;
                                        }
                                    }
                                }*/
                                if(closest.vertex == -1){
                                    for(var j = 0; j < objA.collider.vertexs.length; j++){
                                        var aV = objA.collider.vertexs[j].rrotate(objA.rotation).sum(objA.pos);
                                        var bV;
                                        var p = objB.pos;
                                        if(j == objA.collider.vertexs.length - 1){
                                            bV = objA.collider.vertexs[0].rrotate(objA.rotation).sum(objA.pos);
                                        } else {
                                            bV = objA.collider.vertexs[j+1].rrotate(objA.rotation).sum(objA.pos);
                                        }
                                        //var dist = ((aV.x - p.x)*(bV.y-p.y)-(aV.y-p.y)*(bV.x-p.x))/(bV.substract(aV).magnitude());
                                        var resp = pnt2line(p, aV, bV);
                                        if(resp.dist < objB.collider.radius){
                                            if(closest.distance > resp.dist){
                                                closest.distance = resp.dist;
                                                closest.vertex = j;
                                                closest.dir = resp.nearest.substract(p).normalized();
                                                contactPA = resp.nearest.substract(objA.pos);
                                                contactPB = closest.dir.multiply(objB.collider.radius);
                                            }
                                        }
                                    }
                                }
                                if(closest.vertex != -1){
                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //CIRCLE
                                    sep = closest.dir.multiply(objA.pos.substract(objB.pos).magnitude() - objA.collider.maxRadius - objB.collider.maxRadius);
                                    Na = objB.pos.substract(objA.pos).normalized();

                                    //POLYGON
                                    MTDb = new $e.Vector2(0,0);
                                    Nb = objA.collider.normals[closest.vertex].clone();
                                    Nb.rotate(objA.rotation);
                                }
                            } else {

                                //COLLISION POLYGON-POLYGON
                                var MTD2 = Intersect(obj, objB);
                                if(MTD2.intersect){

                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //A OBJ
                                    var b = FindAxisLeastPenetration(objB, obj);
                                    MTDa = new $e.Vector2(0,0);

                                    Na = objB.collider.normals[b.vertexIndex].clone();
                                    Na.rotate(objB.rotation);

                                    //B objB
                                    var a = FindAxisLeastPenetration(obj, objB);
                                    MTDb = new $e.Vector2(0,0);

                                    Nb = objA.collider.normals[a.vertexIndex].clone();
                                    Nb.rotate(objA.rotation);

                                    sep = MTD2.mtd;
                                }
                            }

                            if(trueCollide){

                                //Reflect velocity
                                var rf = (objA.bounce + objB.bounce)/2;
                                var ja = rf * ((objA.velocity.multiply(objA.mass - objB.mass).sum(objB.velocity.multiply(objB.mass*2)).magnitude())/(objA.mass + objB.mass));
                                var jb = rf * ((objB.velocity.multiply(objB.mass - objA.mass).sum(objA.velocity.multiply(objA.mass*2)).magnitude())/(objB.mass + objA.mass));

                                //A OBJ
                                var Vea = objA.velocity.clone();<commit_msg>Minor Update<commit_after>/*

#DSV:0.5#

Collision Response - http://elancev.name/oliver/2D%20polygon.htm

*/


(function(window){

    'use strict';

    var $e = new function(){

        var constants = {
            //g: 6674*Math.pow(10,-11)
            g: 0.01
        }

        var internal = {
            debug: true,
            started: false,
            canvas: "",
            ctx: "",
            size: 0,
            time: {
                FPS: 1,
                FPScount: 0,
                FPSsum: 0,
                lossedFrames: 0,
                maxFPS: 60,
                minFPS: 20,
                deltaTime: 1,
                startTime: 0,
                behindTime: 0,
                elapsedTime: 0,
                miliseconds: 0,
                speed: 1, //Multiplier
                catchUpTime: 1/8, //S
                intervalClear: 10000, //MS
                elapsedLastClear: 0, //MS
                intervalRestartDelay: 40 //MS
            },
            controlVars: {
                update: {
                    finish: false,
                    exceeded: 0,
                    alert: 50,
                    alerted: false
                },
                start: false
            },
            debugVars: [],
            console: {
                history: [],
                border: false,
                font: '7pt Calibri',
                color: 'black'
            },
            world: {
                gravity: 10,
                drag: 1
            },
            globals: {
                maxForce: 150,
                opacity: 0.9,
                background: "#FFF"
            },
            mouse: 0,
            mouseover: undefined,
            layers: [],
            phycs: [],
            user: {}
        }

        //Public Functions start

        this.init = function(canvas, start, update){
            if(!internal.started){
                if(canvas != undefined && typeof canvas == "object"){
                    if(typeof start == "function" && typeof update == "function"){
                        try {
                            internal.canvas = canvas;
                            internal.ctx = canvas.getContext("2d");
                            internal.size = new this.Vector2(0,0);
                            internal.size.x = canvas.width;
                            internal.size.y = canvas.height;
                            internal.user.start = start;
                            internal.user.udpate = update;
                            Start();
                            internal.started = true;
                        } catch (e) {
                            $d.LogError("The element is not a canvas");
                        }
                    } else {
                        $d.LogError("Missing Start/Update functions");
                    }
                } else {
                    $d.LogError("Invalid element");
                }
            } else {
                $d.LogWarning("The engine is already running!");
            }
        }

        this.setMaxFPS = function(value){
            if(typeof value == "number"){
                internal.time.maxFPS = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.setDebug = function(value){
            if(typeof value == "boolean"){
                internal.debug = value;
            } else {
                $d.LogError("Invalid value, expected boolean");
            }
        }

        this.setSpeed = function(value){
            if(typeof value == "number"){
                internal.time.speed = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.setGravity = function(value){
            if(typeof value == "number"){
                internal.world.gravity = value;
            } else {
                $d.LogError("Invalid value, expected number");
            }
        }

        this.stats = function(){
            $d.Log("STATS");
            $d.Log("FPS: " + internal.time.FPS);
            $d.Log("Current state: " + ((internal.started)? "Running" : "Stopped"));
            $d.Log("Frame loss count: " + internal.time.lossedFrames);
            $d.Log("Elapsed Time: " + $d.FormatMiliseconds(internal.time.elapsedTime));
            $d.Stats();
        }

        this.add2DObject = function(obj, layer){
            var ind = internal.phycs.push(obj);
            if(internal.layers[layer+50] == undefined){
                internal.layers[layer+50] = [];
            }
            internal.layers[layer+50].push(internal.phycs[ind-1]);
        }

        this.addDebugObject = function(name, obj, vars, duration){
            internal.debugVars.push({
                name: name,
                obj: obj,
                vars: vars,
                duration: duration
            });
        }

        this.writeDebugConsole = function(string, type){
            internal.console.history.push({name: string, type: type});
        }

        //close

        //Private functions start

        function Start(){
            internal.controlVars.update.finish = true;
            var sd = new Date().getTime();
            internal.time.miliseconds = sd;
            internal.mouse = new $e.Vector2(0,0);

            //Creates a new console
            internal.console.size = new $e.Vector2(50,50);
            internal.console.position = new $e.Vector2(internal.size.x - internal.console.size.x,0);

            //Creates the object for the mouse position
            internal.mouse = new $e.Object2D("Mouse", 0, 0, 1, 1, 1);
            internal.mouse.setCollider(new $e.BoxCollider(0.1,0.1));

            internal.canvas.addEventListener('mousemove', function(evt) {
                UpdateMousePos(internal.canvas, evt);
            }, false);

            internal.time.interval = StartInterval();
        }

        function StartInterval(){
            return setInterval(function(){
                if(internal.controlVars.update.finish){
                    internal.controlVars.update.finish = false;
                    internal.controlVars.update.alerted = false;
                    internal.controlVars.update.exceeded = 0;

                    var d = new Date().getTime();
                    var t = d - internal.time.miliseconds;
                    internal.time.elapsedTime += t;
                    internal.time.elapsedLastClear += t;
                    internal.time.deltaTime = (t/1000) * internal.time.speed;
                    internal.time.miliseconds = d;
                    internal.time.FPS = 1/(internal.time.deltaTime/internal.time.speed);
                    internal.time.FPSsum += internal.time.FPS;
                    internal.time.FPScount++;
                    if(internal.time.FPS > 0 && internal.time.FPS < internal.time.minFPS){
                        $d.LogWarning("Running behind the main timeline ("+(internal.time.FPS+internal.time.behindTime)+" seconds) trying to catch up at " + internal.time.catchUpTime + " seconds per frame.");
                        internal.time.behindTime += internal.time.deltaTime;
                        internal.time.deltaTime = 1/internal.time.minFPS;
                    } else {
                        if(internal.time.behindTime > 0){
                            if(internal.time.behindTime < internal.time.catchUpTime){
                                internal.time.deltaTime += internal.time.behindTime;
                                internal.time.behindTime = 0;
                                $d.LogWarning("Main timeline reached.");
                            } else {
                                internal.time.deltaTime += internal.time.catchUpTime;
                                internal.time.behindTime -= internal.time.catchUpTime;
                            }
                        }

                    }
                    if(internal.time.elapsedLastClear >= internal.time.intervalClear){
                        internal.time.elapsedLastClear = 0;
                        clearInterval(internal.time.interval);
                        ReStartInterval();
                    } else {
                        Update();
                    }
                } else {
                    internal.controlVars.update.exceeded++;
                    internal.time.lossedFrames++;
                    if(internal.controlVars.update.exceeded >= internal.controlVars.update.alert){
                        $d.LogWarning("Loosing frames, consider using a lower maxFPS("+internal.time.FPS+") value or reivew your code.");
                        internal.controlVars.update.alerted = true;
                    }
                }
            }, 1000/internal.time.maxFPS);
        }

        function ReStartInterval(){
            setTimeout(function(){
                internal.controlVars.update.finish = true;
                internal.time.interval = StartInterval();
            }, internal.time.intervalRestartDelay);
        }

        function Update(){
            try{
                internal.user.udpate();    
            } catch(e){
                $d.LogError("Error in User Update function", e);
            }
            UpdatePhysics();
            DrawObjects();
            DrawFPS();
            DrawMousePosition();
            DrawDebug();
            DrawConsole();
            internal.controlVars.update.finish = true;
        }

        function DrawFPS(){
            internal.ctx.font = "12px Arial";
            internal.ctx.fillStyle = "#000";
            //internal.ctx.fillText("FPS: " + Math.ceil(internal.time.FPSsum / internal.time.FPScount),10,10);
            internal.ctx.fillText("FPS: " + Math.round(internal.time.FPS),10,10);
        }

        function DrawMousePosition(){
            var msg = 'Mouse position: ' + internal.mouse.pos.x + ',' + internal.mouse.pos.y;
            internal.ctx.font = '8pt Calibri';
            internal.ctx.fillStyle = 'black';
            internal.ctx.fillText(msg, 10, 25);
        }

        function DrawConsole(){

        }

        function DrawDebug(){
            var offset = 0;
            for(var i = internal.debugVars.length - 1; i >= 0; i--){
                if(internal.debugVars[i].duration == undefined || internal.debugVars[i].duration > 0){
                    var finalObj = internal.debugVars[i].obj;
                    for(var x = 0; x < internal.debugVars[i].vars.length; x++){
                        finalObj = finalObj[internal.debugVars[i].vars[x]];
                    }
                    var msg = internal.debugVars[i].name + ":\t" + finalObj.toString(2);
                    internal.ctx.font = '7pt Calibri';
                    internal.ctx.fillStyle = 'black';
                    internal.ctx.fillText(msg, 10, internal.size.y - 10*(internal.debugVars.length - i - offset));
                    if(internal.debugVars[i].duration != undefined){
                        internal.debugVars[i].duration--;
                    }
                } else {
                    offset++;
                }
            }
        }

        function UpdateMousePos(canvas, evt) {
            var rect = canvas.getBoundingClientRect();
            internal.mouse.pos.x = evt.clientX - rect.left;
            internal.mouse.pos.y = internal.size.y - evt.clientY + rect.top;
            internal.mouseover = CheckCollision(internal.mouse, true);
        }

        function UpdatePhysics(){
            var objA;
            for(var i = 0; i < internal.phycs.length; i++){

                objA = internal.phycs[i];

                if(!objA.kinematic){
                    CheckCollision(objA, false);  
                    objA.collider.checked = internal.time.elapsedTime;

                    objA.rotation -= objA.angularVelocity * internal.time.deltaTime;

                    objA.velocity.x += objA.force.x/objA.mass;

                    objA.velocity.y -= internal.world.gravity;
                    objA.velocity.y += objA.force.y/objA.mass;

                    objA.pos = objA.pos.sum(objA.velocity.multiply(internal.time.deltaTime));
                    if(objA.newtonian){
                        for(var j = 0; j < internal.phycs.length; j++){
                            var objB = internal.phycs[j];
                            if(objB != objA){
                                if(objB.newtonian){
                                    var dist = objB.pos.substract(objA.pos);
                                    var dir = dist.normalized();
                                    dist = dist.magnitude();
                                    var f = constants.g*((objA.mass * objB.mass)/dist)
                                    dir.scale(f);
                                    objA.addForce(dir.x, dir.y);
                                }
                            }
                        }
                    }
                }
            }
        }

        function CheckCollision(obj, retObj){
            var objA = obj;
            var objB;
            var v3 = new $e.Vector2(0,0);
            for(var i = 0; i < internal.phycs.length; i++){
                objB = internal.phycs[i];
                if(objB.collider.checked != internal.time.elapsedTime || retObj){
                    if(objB != obj && (!objA.kinematic || !objB.kinematic)){
                        v3.x = objB.pos.x - objA.pos.x;
                        v3.y = objB.pos.y - objA.pos.y;
                        var dist = v3.magnitude();
                        dist = dist - objA.collider.maxRadius - objB.collider.maxRadius;
                        //$d.Log(dist);
                        if((dist) < 0){

                            var Na, Nb, sep;
                            var contactPA, contactPB;
                            var trueCollide = false;

                            //COLLISION CIRCLE-CIRCLE
                            if(objA.collider.type == 1 && objB.collider.type == 1){
                                if(retObj) return objB;
                                trueCollide = true;
                                //sep = objB.pos.substract(objA.pos);
                                var dir = objB.pos.substract(objA.pos).normalized();
                                sep = dir.multiply(objA.pos.substract(objB.pos).magnitude() - objA.collider.maxRadius - objB.collider.maxRadius);
                                Na = objA.pos.substract(objB.pos).normalized();
                                Nb = objB.pos.substract(objA.pos).normalized();
                            } else if(objA.collider.type == 1 && (objB.collider.type == 0 || objB.collider.type == 2)){ //COLLISION CIRCLE-POLYGON
                                var closest = {
                                    vertex: -1,
                                    distance: 1000000
                                };
                                /*for(var j = 0; j < objB.collider.vertexs.length; j++){
                                    var sub = objB.collider.vertexs[j].clone().rrotate(objB.rotation).sum(objB.pos).substract(objA.pos);
                                    var dist = sub.magnitude();
                                    var dir = sub.normalized();
                                    if(dist < objA.collider.radius){
                                        if(closest.distance > dist){
                                            closest.distance = dist;
                                            closest.vertex = j;
                                            closest.dir = dir;
                                        }
                                    }
                                }*/
                                if(closest.vertex == -1){
                                    for(var j = 0; j < objB.collider.vertexs.length; j++){
                                        var aV = objB.collider.vertexs[j].rrotate(objB.rotation).sum(objB.pos);
                                        var bV;
                                        var p = objA.pos;
                                        if(j == objB.collider.vertexs.length - 1){
                                            bV = objB.collider.vertexs[0].rrotate(objB.rotation).sum(objB.pos);
                                        } else {
                                            bV = objB.collider.vertexs[j+1].rrotate(objB.rotation).sum(objB.pos);
                                        }
                                        //var dist = (((aV.x - p.x)*(bV.y-p.y))-((aV.y-p.y)*(bV.x-p.x)))/(bV.substract(aV).magnitude());
                                        var resp = pnt2line(p, aV, bV);
                                        if(resp.dist < objA.collider.radius){
                                            if(closest.distance > resp.dist){
                                                closest.distance = resp.dist;
                                                closest.vertex = j;
                                                closest.dir = resp.nearest.substract(p).normalized();
                                                contactPB = resp.nearest.substract(objB.pos);
                                                contactPA = closest.dir.multiply(objA.collider.radius);
                                            }
                                        }
                                    }
                                }
                                if(closest.vertex != -1){
                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //CIRCLE
                                    sep = closest.dir.multiply(objB.pos.substract(objA.pos).magnitude() - objB.collider.maxRadius - objA.collider.maxRadius);
                                    Na = objA.pos.substract(objB.pos).normalized();

                                    //POLYGON
                                    MTDb = new $e.Vector2(0,0);
                                    Nb = objB.collider.normals[closest.vertex].clone();
                                    Nb.rotate(objB.rotation);
                                }
                            } else if((objA.collider.type == 0 || objA.collider.type == 2) && objB.collider.type == 1){ //COLLISION POLYGON-CIRCLE
                                var closest = {
                                    vertex: -1,
                                    distance: 1000000
                                };
                                /*for(var j = 0; j < objA.collider.vertexs.length; j++){
                                    var sub = objA.collider.vertexs[j].clone().rrotate(objA.rotation).sum(objA.pos).substract(objB.pos);
                                    var dist = sub.magnitude();
                                    var dir = sub.normalized();
                                    if(dist < objB.collider.radius){
                                        if(closest.distance > dist){
                                            closest.distance = dist;
                                            closest.vertex = j;
                                            closest.dir = dir;
                                        }
                                    }
                                }*/
                                if(closest.vertex == -1){
                                    for(var j = 0; j < objA.collider.vertexs.length; j++){
                                        var aV = objA.collider.vertexs[j].rrotate(objA.rotation).sum(objA.pos);
                                        var bV;
                                        var p = objB.pos;
                                        if(j == objA.collider.vertexs.length - 1){
                                            bV = objA.collider.vertexs[0].rrotate(objA.rotation).sum(objA.pos);
                                        } else {
                                            bV = objA.collider.vertexs[j+1].rrotate(objA.rotation).sum(objA.pos);
                                        }
                                        //var dist = ((aV.x - p.x)*(bV.y-p.y)-(aV.y-p.y)*(bV.x-p.x))/(bV.substract(aV).magnitude());
                                        var resp = pnt2line(p, aV, bV);
                                        if(resp.dist < objB.collider.radius){
                                            if(closest.distance > resp.dist){
                                                closest.distance = resp.dist;
                                                closest.vertex = j;
                                                closest.dir = resp.nearest.substract(p).normalized();
                                                contactPA = resp.nearest.substract(objA.pos);
                                                contactPB = closest.dir.multiply(objB.collider.radius);
                                            }
                                        }
                                    }
                                }
                                if(closest.vertex != -1){
                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //CIRCLE
                                    sep = closest.dir.multiply(objA.pos.substract(objB.pos).magnitude() - objA.collider.maxRadius - objB.collider.maxRadius);
                                    Na = objB.pos.substract(objA.pos).normalized();

                                    //POLYGON
                                    MTDb = new $e.Vector2(0,0);
                                    Nb = objA.collider.normals[closest.vertex].clone();
                                    Nb.rotate(objA.rotation);
                                }
                            } else {

                                //COLLISION POLYGON-POLYGON
                                var MTD2 = Intersect(obj, objB);
                                if(MTD2.intersect){

                                    if(retObj) return objB;
                                    trueCollide = true;

                                    //A OBJ
                                    var b = FindAxisLeastPenetration(objB, obj);
                                    MTDa = new $e.Vector2(0,0);

                                    Na = objB.collider.normals[b.vertexIndex].clone();
                                    Na.rotate(objB.rotation);
                                    contactPA = objA.collider.vertexs[a.vertexIndex];

                                    //B objB
                                    var a = FindAxisLeastPenetration(obj, objB);
                                    MTDb = new $e.Vector2(0,0);

                                    Nb = objA.collider.normals[a.vertexIndex].clone();
                                    Nb.rotate(objA.rotation);
                                    contactPB = objB.collider.vertexs[b.vertexIndex];

                                    sep = MTD2.mtd;
                                }
                            }

                            if(trueCollide){

                                //Reflect velocity
                                var rf = (objA.bounce + objB.bounce)/2;
                                var ja = rf * ((objA.velocity.multiply(objA.mass - objB.mass).sum(objB.velocity.multiply(objB.mass*2)).magnitude())/(objA.mass + objB.mass));
                                var jb = rf * ((objB.velocity.multiply(objB.mass - objA.mass).sum(objA.velocity.multiply(objA.mass*2)).magnitude())/(objB.mass + objA.mass));

                                //A OBJ
                                var Vea = objA.velocity.clone();
{'fraction_non_alphanumeric': 0.105706641949822, 'fraction_numerical': 0.008384066061384846, 'mean_word_length': 0.9040590405904059, 'pattern_counts': {'":': 2, '<': 33, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 338, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1034050', 'n_tokens_mistral': 12009, 'n_tokens_neox': 11116, 'n_words': 1904}