Adds vector and math utils for bullet collisions
authordsc <david.schoonover@gmail.com>
Thu, 4 Nov 2010 23:17:39 +0000 (16:17 -0700)
committerdsc <david.schoonover@gmail.com>
Thu, 4 Nov 2010 23:17:39 +0000 (16:17 -0700)
src/portal/layer.js
src/portal/math/line.js [new file with mode: 0644]
src/portal/math/math.js [new file with mode: 0644]
src/portal/math/vec.js [new file with mode: 0644]
src/portal/shape.js
src/portal/util/loc.js
src/tanks/ui.js
src/tanks/util/grid.js
test/math/index.php [new file with mode: 0644]
test/math/math.test.js [new file with mode: 0644]

index 62e2da1..f88500a 100644 (file)
@@ -131,11 +131,11 @@ Layer = new Y.Class('Layer', {
         
         var nb = this.negBleed.x
         ,   v  = this.canvasWidth = w + nb + this.posBleed.x;
-        this.canvas[0].width = v;
         this.canvas.css({
             'width' : v+'px',
             'margin-left' : (-nb)+'px'
         });
+        this.canvas[0].width = v;
         
         return this;
     },
@@ -149,11 +149,11 @@ Layer = new Y.Class('Layer', {
         
         var nb = this.negBleed.y
         ,   v  = this.canvasHeight = h + nb + this.posBleed.y;
-        this.canvas[0].height = v;
         this.canvas.css({
             'height' : v+'px',
             'margin-top' : (-nb)+'px'
         });
+        this.canvas[0].height = v;
         
         return this;
     },
@@ -438,7 +438,7 @@ function makeDelegate(name, dirties, prop){
 $(function(){
     $('<style />')
         .text([
-            '.portal.layer { position:absolute; z-index:1; top:0; left:0; }',
+            '.portal.layer { position:absolute; z-index:1; top:0; left:0; line-height:0; }',
             '.portal.layer canvas { z-index:0; }'
         ].join('\n'))
         .appendTo('head');
diff --git a/src/portal/math/line.js b/src/portal/math/line.js
new file mode 100644 (file)
index 0000000..7229216
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * A line.
+ */
+math.Line = new Y.Class('Line', math.Vec, {
+    
+    init : function init(x1,y1, x2,y2, t){
+        this.t  = 1/(t || 1);
+        this.x1 = x1; this.y1 = y1;
+        this.x2 = x2; this.y2 = y2;
+        
+        var xdelta = x2-x1, ydelta = y2-y1
+        ,    m = this.slope  =  ydelta/xdelta
+        ,   yi = this.yint   = -x1*m + y1
+        ,   xi = this.xint   = -y1/m + x1
+        ;
+        
+        this.pa = this.t*xdelta;
+        this.pb = this.t*ydelta
+        math.Vec.init.call(this, xdelta, ydelta);
+    },
+    
+    pcalc : function parametric(t){
+        return new math.Vec( this.x1 + this.pa*t ,
+                             this.y1 + this.pb*t );
+    },
+    
+    calcY : function calcY(x){
+        return x*this.slope + this.yint;
+    },
+    
+    calcX : function calcX(y){
+        return y/this.slope + this.xint;
+    },
+    
+    toString : function toString(){
+        return 'Line('+this.x1+','+this.y1+', '+this.x2+','+this.y2+', slope='+this.slope+')';
+    }
+    
+});
\ No newline at end of file
diff --git a/src/portal/math/math.js b/src/portal/math/math.js
new file mode 100644 (file)
index 0000000..072bd28
--- /dev/null
@@ -0,0 +1,9 @@
+math = {
+    clamp : function clamp(value, min, max) {
+      return Math.min(Math.max(value, min), max);
+    },
+    
+    lerp : function lerp(x, a, b) {
+        return a + x*(b - a);
+    }
+};
\ No newline at end of file
diff --git a/src/portal/math/vec.js b/src/portal/math/vec.js
new file mode 100644 (file)
index 0000000..84b90a3
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * A 2-dimensional vector.
+ */
+math.Vec = new Y.Class('Vec', {
+    
+    init : function init(x, y){
+        if ( Array.isArray(x) ) {
+            y = x[1];
+            x = x[0];
+        }
+        
+        this.x = x;
+        this.y = y;
+    },
+    
+    equals : function equals(b){
+        return (this.x === b.x) && (this.y === b.y);
+    },
+    
+    clone : function clone(){
+        return new Vec(this.x, this.y);
+    },
+    
+    scale : function scale(s){
+        this.x *= s;
+        this.y *= s;
+        return this;
+    },
+    
+    invert : function invert(){
+        this.x = -this.x;
+        this.y = -this.y;
+        return this;
+    },
+    
+    normalize : function normalize(){
+        return this.scale(1 / this.magnitude());
+    },
+    
+    add : function add(b){
+        this.x += b.x;
+        this.y += b.y;
+        return this;
+    },
+    
+    subtract : function subtract(b){
+        this.x -= b.x;
+        this.y -= b.y;
+        return this;
+    },
+    
+    magnitude : function magnitude(){
+        return Math.sqrt(this.x*this.x + this.y*this.y);
+    },
+    
+    sqMagnitude : function sqMagnitude(){
+        return this.x*this.x + this.y*this.y;
+    },
+    
+    dot : function dot(b){
+        return this.x*b.x + this.y*b.y;
+    },
+    
+    toString : function toString(){
+        return 'Vec('+this.x+','+this.y+')';
+    }
+    
+});
+
+Y.extend(math.Vec, {
+    sum : function sum(a, b) {
+        return new Vec(a.x+b.x, a.y+b.y);
+    },
+    
+    difference : function difference(a, b) {
+        return new Vec(a.x-b.x, a.y-b.y);
+    },
+    
+    dot : function dot(a, b) {
+        return a.x*b.x + a.y*b.y;
+    },
+    
+    lerp : function lerp(x, a, b) {
+        return new Vec( math.lerp(a.x, b.x, x),
+                        math.lerp(a.y, b.y, x)  );
+    },
+    
+    reflect : function reflect(v, line){
+        return line.clone()
+            .scale(2 * v.dot(line) / line.dot(line))
+            .subtract(v);
+    }
+    
+});
index 02a9a7a..2b269de 100644 (file)
@@ -65,6 +65,26 @@ Rect = new Y.Class('Rect', Shape, {
     
 });
 
+Circle = new Y.Class('Circle', Shape, {
+    _cssClasses : 'portal layer shape circle',
+    
+    init : function init(radius){
+        Layer.init.call(this);
+        
+        var d = radius * 2;
+        this.radius = radius;
+        this.width(d).height(d)
+            .origin(radius,radius);
+    },
+    
+    drawShape : function drawShape(ctx){
+        var r  = this.radius;
+        ctx.arc(r,r, r, 0, Math.PI*2, false);
+        ctx.fill();
+    }
+    
+});
+
 Polygon = new Y.Class('Polygon', Shape, {
     _cssClasses : 'portal layer shape polygon',
     
@@ -96,6 +116,19 @@ Polygon = new Y.Class('Polygon', Shape, {
     }
 });
 
+// Er, this won't do. It's only a line-segment.
+Line = new Y.Class('Line', Polygon, {
+    
+    init : function init(x,y){
+        Polygon.init.call(this, [x], [y]);
+    }
+    
+});
+Line.fromPoints = function fromPoints(x1,y1, x2,y2){
+    return new Line(x2-x1, y2-y1).position(x1,y1);
+};
+
+
 Triangle = new Y.Class('Triangle', Polygon, {
     _cssClasses : 'portal layer shape polygon triangle',
     
@@ -125,27 +158,3 @@ Quad = new Y.Class('Quad', Polygon, {
         Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]);
     }
 });
-
-Circle = new Y.Class('Circle', Shape, {
-    _cssClasses : 'portal layer shape circle',
-    
-    init : function init(radius){
-        Layer.init.call(this);
-        
-        var d = radius * 2;
-        this.radius = radius;
-        this.width(d)
-            .height(d)
-            .center(r,r);
-    },
-    
-    drawShape : function drawShape(ctx){
-        var r  = this.radius
-        // ,   cx = -this.originX
-        // ,   cy = -this.originY ;
-        // ctx.arc(cx,cy, r, 0, Math.PI*2, false);
-        ctx.arc(r,r, r, 0, Math.PI*2, false);
-        ctx.fill();
-    }
-    
-});
\ No newline at end of file
index 5e400c3..d170dc8 100644 (file)
@@ -141,15 +141,30 @@ Loc.Rect = new Y.Class('Rect', [], {
     
     attr : Y.attr.methodize(),
     
-    top    : function top(x1,y1){
+    top : function top(x1,y1){
         if ( x1 !== undefined && y1 !== undefined )
             return new Loc(this.x1, this.y1);
         else
             return this.attr({ 'x1':x1, 'y1': y1 });
     },
-    bottom : function bottom(){ return new Loc(this.x2, this.y2); },
-    left   : function left(){   return new Loc(this.x1, this.y1); },
-    right  : function right(){  return new Loc(this.x2, this.y2); },
+    bottom : function bottom(x2,y2){
+        if ( x2 !== undefined && y2 !== undefined )
+            return new Loc(this.x2, this.y2);
+        else
+            return this.attr({ 'x2':x2, 'y2': y2 });
+    },
+    left : function left(x1,y1){
+        if ( x1 !== undefined && y1 !== undefined )
+            return new Loc(this.x1, this.y1);
+        else
+            return this.attr({ 'x1':x1, 'y1': y1 });
+    },
+    right : function right(x2,y2){
+        if ( x2 !== undefined && y2 !== undefined )
+            return new Loc(this.x2, this.y2);
+        else
+            return this.attr({ 'x2':x2, 'y2': y2 });
+    },
     
     moveTo : function moveTo(x,y){
         return new Loc.Rect(x,y, x+this.width,y+this.height);
index a5473d3..5b67993 100644 (file)
@@ -49,6 +49,7 @@ jQuery(function($){
     // Tick once to draw grid, initial units
     LBT.start();
     LBT.stop();
+    LBT.loop.elapsedAtStop = 0;
     
     setInterval(updateInfo, 1000);
     updateInfo();
index 497da96..22cf5c1 100644 (file)
@@ -1,6 +1,9 @@
 Grid = new Y.Class('Grid', Rect, {
     _cssClasses : 'portal layer shape rect grid',
     
+    lineWidth : 0.5,
+    strokeStyle : '#6E6E6E',
+    
     init : function init(cols,rows, size){
         this.cols = cols;
         this.rows = rows;
@@ -14,25 +17,19 @@ Grid = new Y.Class('Grid', Rect, {
         ,   cols =  this.cols
         ,   w    =  this.canvasWidth
         ,   h    =  this.canvasHeight
-        // ,   cx   = -this.originX
-        // ,   cy   = -this.originY
         ;
         
-        ctx.lineWidth   = 0.5;
-        ctx.strokeStyle = '#6E6E6E';
+        ctx.lineWidth   = this.lineWidth;
+        ctx.strokeStyle = this.strokeStyle;
         
         for (var row=0, y=0; row<=rows; y = (++row) * size){
             ctx.moveTo(0, y);
             ctx.lineTo(w, y);
-            // ctx.moveTo(cx,   cy+y);
-            // ctx.lineTo(cx+w, cy+y);
         }
         
         for (var col=0, x=0; col<=cols; x = (++col) * size){
             ctx.moveTo(x, 0);
             ctx.lineTo(x, h);
-            // ctx.moveTo(cx+x, cy);
-            // ctx.lineTo(cx+x, cy+h);
         }
         
         ctx.stroke();
diff --git a/test/math/index.php b/test/math/index.php
new file mode 100644 (file)
index 0000000..3fc9c2e
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>math</title>
+<script type="text/javascript">document.write('<base href="http://'+window.location.host+'/">');</script>
+<link rel="stylesheet" href="css/reset.css" type="text/css" media="screen">
+<style type="text/css" media="screen">
+
+html, body { width:100%; height:100%;
+    font-family:Geogrotesque,Helvetica; color:#999; background-color:#D2E1A7; }
+body { font-family:Geogrotesque, Helvetica; font-size:12pt; }
+h1 { position:fixed; top:0; right:0; margin:0; padding:0; font-size:3em; color:#000; opacity:0.25; z-index:100; }
+ul, ol, li { list-style: none ! important; margin:0; padding:0; }
+
+.rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
+
+#plot { position:relative; top:1em; width:1000px; height:600px; margin:0 auto; background-color:#fff; }
+
+</style>
+</head>
+<body>
+
+<div id="plot"></div>
+
+<div id="scripts">
+<?php
+$scripts = array(
+    "lib/jquery-1.4.3.js",
+    "lib/jquery.sparkline.min.js",
+    "lib/jquery.hotkeys.js",
+    
+    // "http://static.ukijs.org/pkg/0.3.8/uki.js",
+    // "http://static.ukijs.org/pkg/0.3.8/uki-more.js",
+    
+    "src/lessly/future.js",
+    
+    "src/Y/y.js.php",
+    "src/Y/modules/y.event.js",
+    
+    "src/evt/evt.class.js",
+    
+    "src/portal/util/loc.js",
+    "src/portal/layer.js",
+    "src/portal/shape.js",
+    "src/tanks/util/grid.js",
+    
+    "src/portal/math/math.js",
+    "src/portal/math/vec.js",
+    "src/portal/math/line.js",
+    
+    "test/math/math.test.js"
+);
+
+function js($src) {
+    echo "    <script src=\"$src\" type=\"text/javascript\"></script>\n";
+}
+
+foreach ($scripts as $s) js($s);
+?>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/test/math/math.test.js b/test/math/math.test.js
new file mode 100644 (file)
index 0000000..ee96a8b
--- /dev/null
@@ -0,0 +1,79 @@
+PPU = 25;
+PX  = 2.5/PPU;
+
+$(function(){
+
+plot = $('#plot');
+
+w = plot.width();  w2 = w/2;
+h = plot.height(); h2 = h/2;
+W = w/PPU; W2 = W/2;
+H = h/PPU; H2 = H/2;
+
+grid = new Grid( W, H, PPU ).appendTo(plot);
+grid.lineWidth = 1.0;
+grid.strokeStyle = '#E0E0E0'; //'#EEEEEE'; //
+grid.draw();
+
+P = new Layer()
+    .width(w).height(h)
+    .appendTo(grid);
+
+ctx = P.ctx;
+ctx.translate(w2, h2);
+ctx.scale(PPU, PPU);
+
+// Draw axes
+drawLine(-W2,0, W2,0, '#CCCCCC');
+drawLine(0,-H2, 0,H2, '#CCCCCC');
+
+testLine(1,2, 4,7,    'rgba(231,48,117, 0.5)',  'rgba(69,150,255, 1)');
+// testLine(-4,-2, 7,13, 'rgba(131,187,50, 0.75)', 'rgba(69,150,255, 1)');
+
+});
+
+function testLine(x1,y1, x2,y2, color, pcolor){
+    var t,p, line = new math.Line(x1,y1, x2,y2, 10);
+    drawLine(-W/2, line.calcY(-W2), line.calcX(H2), H2, color);
+    drawPoint(line.x1, line.y1);
+    drawPoint(line.x2, line.y2);
+    
+    drawPoint(0, 0, pcolor);
+    
+    t = 0;
+    p = line.pcalc(t);
+    drawPoint(p.x, p.y, pcolor);
+    
+    t = 1;
+    p = line.pcalc(t);
+    drawPoint(p.x, p.y, pcolor);
+    
+    t = -1;
+    p = line.pcalc(t);
+    drawPoint(p.x, p.y, pcolor);
+    
+    return line;
+}
+
+
+function drawLine(x1,y1, x2,y2, color, width){
+    ctx.beginPath();
+    ctx.lineWidth = width || PX;
+    ctx.strokeStyle = color || '#000000';
+    ctx.moveTo(x1, -y1);
+    ctx.lineTo(x2, -y2);
+    ctx.stroke();
+    ctx.closePath();
+}
+
+function drawPoint(x,y, color, r){
+    r = r || 3.75;
+    var c = new Circle(r)
+        .position(w2-r + x*PPU, h2-r - y*PPU)
+        .attr({ 'fillStyle': color || 'rgba(0,0,0,0.5)' })
+        .appendTo(P)
+        .draw();
+    c.layer.attr('title', '('+x+','+y+')');
+    return c;
+}
+