title: Game object refuses to show on the canvas
body: <p>Here are the things I know:</p>
  
  <p>(1) The ship object is getting rendered to some degree, because I put an extra circle in its render code and that's being drawn.</p>
  
  <p>(2) There's nothing wrong (at least, nothing obvious to me) with my rotation methods, as the numbers show up correctly in the console. And the points <code>top.x</code>, <code>top.y</code>, <code>bottomLeft.x</code>, <code>bottomLeft.y</code>, <code>bottomRight.x</code>, and <code>bottomRight.y</code>are all the numbers they should be.</p>
  
  <p>(3) The actual code using the variables mentioned in (2) to draw the ship is working OK. When I replace the variables by hard-coded numbers, the ship is drawn. So what gives?</p>
  
  <p>The faulty code is in the Ship object at line 140 and you can test it yourself here: <a href="http://noetherherenorthere.com/practice/landscape.html" rel="nofollow">http://noetherherenorthere.com/practice/landscape.html</a>.</p>
  
  <pre><code>  1 /* landscape.js */
    2 
    3 var canvas;
    4 var context;
    5 var landscape;
    6 var ship;
    7 
    8 function init(){
    9 
   10     canvas = document.getElementById('canvas');
   11     context = canvas.getContext('2d');
   12     landscape = new Landscape();
   13     ship = new Ship(375, 400);
   14     animate();
   15     
   16     window.addEventListener('keydown', function(e){
   17         switch(e.keyCode){
   18             case 37: // left
   19                 ship.theta -= 0.1;
   20                 ship.theta %= 2*Math.PI;
   21                 break;
   22             case 38: // up
   23                 ship.v_x += 1*Math.cos(ship.theta);
   24                 ship.v_y -= 1*Math.sin(ship.theta);
   25                 break;
   26             case 39: // right
   27                 ship.theta += 0.1;
   28                 ship.theta %= 2*Math.PI;
   29                 break;
   30             case 40: // down
   31                 // do nothing
   32                 break;
   33             default:
   34         }
   35     });
   36     
   37 }
   38 
   39 function animate(){
   40     
   41     if(this.i == null){
   42         this.i = 0;
   43     } else{
   44         this.i = (this.i + 0.1)%628; // switch it out after 100*2*PI cycles so i doesn't get too big.
   45     }
   46     
   47     landscape.sun.y += 10*Math.sin(this.i);
   48     landscape.moon.y -= 10*Math.cos(this.i);
   49     ship.v = Math.sqrt(Math.pow(ship.v_x, 2) + Math.pow(ship.v_y, 2)); 
   50     ship.x += (0.1 * ship.v_x);
   51     ship.y += (0.1 * ship.v_y);
   52     if(ship.x &lt; 0){
   53         ship.x = canvas.width;
   54     }
   55     if(ship.x &gt; canvas.width){
   56         ship.x = 0;
   57     }
   58     if(ship.y &lt; 0){
   59         ship.y = canvas.height;
   60     }
   61     if(ship.y &gt; canvas.height){
   62         ship.y = 0;
   63     }
   64     if(ship.v &gt; ship.vMax){
   65         ship.v = ship.vMax;
   66     }
   67     if(ship.v &lt; -ship.vMax){
   68         ship.v = -ship.vMax;
   69     }
   70     
   71     context.clearRect(0, 0, canvas.width, canvas.height);
   72     
   73     landscape.render();
   74     
   75     ship.render();
   76     
   77     // draw the dialog box  
   78     context.font = "14px Verdana";
   79     context.fillStyle = 'DodgerBlue';
   80     context.fillText("Velocity: (x: " + this.ship.v_x.toFixed(2) + ", y: " + this.ship.v_y.toFixed(2) + 
   81         ", angle: " + radToDeg(this.ship.theta).toFixed(2) + ")", 420, 465);
   82     context.fillText("Position: (x: " + this.ship.x.toFixed(2) + ", y: " + this.ship.y.toFixed(2) + ")", 420, 485);
   83     
   84     window.setTimeout(animate, 40);
   85     
   86 }
   87 
   88 function radToDeg(radians){
   89 
   90     return radians*(180/Math.PI);
   91     
   92 }
   93 
   94 function degToRad(degrees){
   95     
   96     return degrees*(Math.PI/180);
   97     
   98 }
   99 
  100 function Landscape(){
  101 
  102     // order of the elements matters here
  103 
  104     this.sky = new Sky();
  105     this.sun = new Sun(600, 150, 50);
  106     this.moon = new Moon(100, 100, 50);
  107     
  108     this.render = function(){
  109     
  110         for(var element in this){
  111             if(this[element].hasOwnProperty("render")){
  112                 this[element].render();
  113             }
  114         }
  115     
  116     }
  117 }
  118 
  119 function Ship(x, y){
  120     
  121     this.x = x;
  122     this.y = y;
  123     this.r = 10;
  124     this.v = 0; // initial velocity of zero
  125     this.v_x = 0;
  126     this.v_y = 0;
  127     this.theta = Math.PI/2; // starts out pointing upwards
  128     this.vMax = 200;
  129     this.render = function(){
  130         var top = new Vector(this.x, this.y);
  131         var bottomLeft = new Vector(this.x - 10, this.y + 30);
  132         var bottomRight = new Vector(this.x + 10, this.y + 30);
  133         
  134         top.rotate(this.theta, true);
  135         bottomLeft.rotate(this.theta, true);
  136         bottomRight.rotate(this.theta, true);
  137         
  138         // console.log("top: " + top + ", bottomLeft: " + bottomLeft + ", bottomRight: " + bottomRight);
  139         
  140         context.fillStyle = 'SlateGray';
  141         context.beginPath();
  142         context.moveTo(top.x, top.y);
  143         context.lineTo(bottomLeft.x, bottomLeft.y);
  144         context.lineTo(bottomRight.x, bottomRight.y);
  145         context.closePath();
  146         context.fill();
  147         
  148         // this code works even though the code above doesn't.
  149         // context.fillStyle = 'SlateGray';
  150 //          context.beginPath();
  151 //          context.moveTo(this.x, this.y);
  152 //          context.lineTo(this.x - 10, this.y + 30);
  153 //          context.lineTo(this.x + 10, this.y + 30);
  154 //          context.closePath();
  155 //          context.fill();
  156         
  157         context.fillStyle = 'LightGreen';
  158         context.beginPath();
  159         context.arc(200, 200, 30, 0, 2*Math.PI);
  160         context.closePath();
  161         context.fill();
  162     }
  163     
  164     function Vector(x, y){
  165         
  166         this.x = x;
  167         this.y = y;
  168         this.rotate = function(theta, round){
  169             
  170             var rotationMatrix = new Matrix(2, 2, Math.cos(theta), -Math.sin(theta), Math.sin(theta), Math.cos(theta));
  171             var vector = new Matrix(2, 1, this.x, this.y);
  172             var resultVector = Matrix.multiply(rotationMatrix, vector);
  173             this.x = resultVector[0][0];
  174             this.y = resultVector[1][0];
  175             if(round){
  176                 this.x = Math.floor(this.x);
  177                 this.y = Math.floor(this.y);
  178             }
  179             
  180             return this;
  181             
  182         }
  183         
  184         this.toString = function(){
  185             
  186             return "x: " + x + ", y: " + y;
  187             
  188         }
  189         
  190     }
  191     
  192     function Matrix(rows, cols /*, var args */){
  193     
  194         // constructor
  195         
  196         if(rows == null || cols == null){
  197             throw new Error("null rows or cols argument");
  198         } else if(!isPositiveInteger(rows) || !isPositiveInteger(cols)){
  199             throw new Error("rows and cols must be whole numbers");
  200         } else if(rows &gt; 1000 || cols &gt; 1000){
  201             throw new Error("rows and cols must be &lt; 1000 in size");
  202         }
  203         
  204         this.numRows = rows;
  205         this.numCols = cols;
  206         if(arguments.length - 2 &gt; rows*cols){
  207             throw new Error("too many arguments to Matrix constructor");
  208         } else if(arguments.length &gt; 2 &amp;&amp; arguments.length - 2 &lt; rows*cols){
  209             throw new Error("too few arguments to Matrix constructor for initializing Matrix." + 
  210                 " Usage: rows, cols [, row-major list of row and col entries]");
  211         }
  212         
  213         if(rows === undefined){
  214             console.log(arguments);
  215         }
  216         
  217         for(var a = 0; a &lt; rows; a++){
  218             this[a] = new Array();
  219         }
  220         
  221         for(var a = 2; a &lt; arguments.length; a++){
  222             try{
  223                 var row = Math.floor((a - 2)/rows);
  224                 var col = Math.floor((a - 2)%rows);
  225                 this[row][col] = arguments[a];
  226             } catch(e){
  227                 console.log(row + ", " + col);
  228                 throw e;
  229             }
  230         }   
  231         
  232     }
  233     
  234     Matrix.multiply = function(matrixA, matrixB){
  235         
  236         if(matrixA.numCols != matrixB.numRows){
  237             throw new Error("# of cols in first matrix must equal # of rows in second matrix");
  238         }
  239     
  240         var resultMatrix = new Matrix(matrixA.numRows, matrixB.numCols);
  241         var sum, i, j, k;
  242         
  243         for(i = 0; i &lt; matrixA.numRows; i++){
  244             for(j = 0; j &lt; matrixB.numCols; j++){
  245                 sum = 0;
  246                 for(k = 0; k &lt; matrixA.numCols; k++){
  247                     sum += matrixA[i][k] * matrixB[k][j];
  248                 }
  249                 resultMatrix[i][j] = sum;
  250             }
  251         }
  252         
  253         return resultMatrix;
  254     }
  255     
  256     function isPositiveInteger(n){
  257     
  258         if(n == null){ return false; }
  259         if(typeof n != "number"){ return false; }
  260         if(!isFinite(n)){ return false; }
  261         if(n &lt;= 0){ return false; }
  262         if(n%1 !== 0){ return false; }
  263         return true;
  264     
  265     }
  266                         
  267 }
  268 
  269 function Sky(){
  270 
  271     this.x = 0;
  272     this.y = 0;
  273     this.width = canvas.width;
  274     this.height = canvas.height;
  275     this.render = function(){
  276         context.fillStyle = 'Black'; // previously Indigo
  277         context.fillRect(this.x, this.y, this.width, this.height);
  278     }
  279     
  280 }
  281 
  282 function Sun(x, y, radius){
  283 
  284     this.x = x;
  285     this.y = y;
  286     this.r = radius;
  287     this.render = function(){
  288         context.fillStyle = 'Gold';
  289         context.beginPath();
  290         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
  291         context.closePath();
  292         context.fill();
  293     }
  294     
  295 }
  296 
  297 function Moon(x, y, radius){
  298 
  299     this.x = x;
  300     this.y = y;
  301     this.r = radius;
  302     this.render = function(){
  303         context.fillStyle = 'LightYellow';
  304         context.beginPath();
  305         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
  306         context.closePath();
  307         context.fill();
  308     }
  309     
  310 }
  </code></pre>