Commits

Cat's Eye Technologies committed 23328c9

Use modern yoob.js (Playfield{HTML,Canvas}View), depict on canvas.

Comments (0)

Files changed (6)

 #########F#
 </textarea>
 
-<pre id="output" style="border: 1px solid blue">
-</pre>
+<div id="display_container">
+  Depict with:
+  <select id="select_depiction">
+    <option>text</option>
+    <option>canvas</option>
+  </select>
+  
+  <pre id="display_text"></pre>
+  <canvas id="display_canvas" width="400" height="250">
+    There would be an HTML5 canvas here, but your browser isn't displaying it.
+  </canvas>
+</div>
 
 </body>
 <script src="yoob/controller.js"></script>
 <script src="yoob/playfield.js"></script>
+<script src="yoob/playfield-html-view.js"></script>
+<script src="yoob/playfield-canvas-view.js"></script>
 <script src="../script/jaccia.js"></script>
 <script>
-  var output = document.getElementById('output');
+  var pf;
+
+  // Text view
+  var displayText = document.getElementById('display_text');
+  var htmlView = new yoob.PlayfieldHTMLView().init(pf, displayText);
+  htmlView.render = function(state) {
+      return dumpMapper(state);
+  };
+
+  // Canvas view
+  var displayCanvas = document.getElementById('display_canvas');
+  var colourMap = {
+      'Space': '#ffffff',
+      'Wall':  '#000000',
+      'Slime': '#00a000',
+      'Food2': '#ff0000',
+      'Food':  '#0000ff'
+  };
+  var canvasView = new yoob.PlayfieldCanvasView().init(pf, displayCanvas);
+  canvasView.drawCell = function(ctx, value, playfieldX, playfieldY,
+                                 canvasX, canvasY, cellWidth, cellHeight) {
+      ctx.fillStyle = colourMap[value] || '#ffffff';
+      ctx.fillRect(canvasX, canvasY, cellWidth, cellHeight);
+  };
+
+  // "View Manager"
+  var currentView = 'text';
+  var views = {
+      'text': htmlView,
+      'canvas': canvasView
+  };
+  var draw = function() {
+      views[currentView].pf = pf;
+      views[currentView].draw();
+  };
+
+  var selectDepiction = document.getElementById('select_depiction');
+  selectDepiction.onchange = function() {
+      var value = selectDepiction.options[selectDepiction.selectedIndex].value;
+      if (value === 'text') {
+          displayText.style.display = 'block';
+          displayCanvas.style.display = 'none';
+      } else {
+          displayText.style.display = 'none';
+          displayCanvas.style.display = 'block';
+      }
+      currentView = value;
+      draw();
+  };
+
+  // Controller.  We don't subclass, we just monkeypatch.
   var c = new yoob.Controller();
-  var pf;
-  
+
   c.load = function(text) {
       pf = new yoob.Playfield();
       pf.setDefault('Space');
       pf.load(0, 0, text, loadMapper);
-      output.innerHTML = pf.dump(dumpMapper);
+      draw();
   };
 
   c.step = function() {
       newPf.setDefault('Space');
       evolve_playfield(pf, newPf);
       pf = newPf;
-      output.innerHTML = pf.dump(dumpMapper);
+      draw();
   };
 
   c.connect({
     'edit': 'edit',
     'speed': 'speed',
     'source': 'input',
-    'display': 'output'
+    'display': 'display_container'
   });
+  
   c.click_load();
 </script>

demo/jacciata.html

 ##F######
 </textarea>
 
-<pre id="output" style="border: 1px solid blue">
-</pre>
+<div id="display_container">
+  Depict with:
+  <select id="select_depiction">
+    <option>text</option>
+    <option>canvas</option>
+  </select>
+  
+  <pre id="display_text"></pre>
+  <canvas id="display_canvas" width="400" height="250">
+    There would be an HTML5 canvas here, but your browser isn't displaying it.
+  </canvas>
+</div>
 
 </body>
 <script src="yoob/controller.js"></script>
 <script src="yoob/playfield.js"></script>
+<script src="yoob/playfield-html-view.js"></script>
+<script src="yoob/playfield-canvas-view.js"></script>
 <script src="../script/jacciata.js"></script>
 <script>
-  var output = document.getElementById('output');
+  var pf;
+
+  // Text view
+  var displayText = document.getElementById('display_text');
+  var htmlView = new yoob.PlayfieldHTMLView().init(pf, displayText);
+  htmlView.render = function(state) {
+      return dumpMapper(state);
+  };
+
+  // Canvas view
+  var displayCanvas = document.getElementById('display_canvas');
+  var colourMap = {
+      'Space':  '#ffffff',
+      'Wall':   '#000000',
+      'Slime':  '#00a000',
+      'Solved': '#00ff40',
+      'Finish': '#0000ff',
+      'Start':  '#ff0000',
+      'Head':   '#a0c0a0',
+      'Body':   '#80a080'
+  };
+  var canvasView = new yoob.PlayfieldCanvasView().init(pf, displayCanvas);
+  canvasView.drawCell = function(ctx, value, playfieldX, playfieldY,
+                                 canvasX, canvasY, cellWidth, cellHeight) {
+      ctx.fillStyle = colourMap[value] || '#ffffff';
+      ctx.fillRect(canvasX, canvasY, cellWidth, cellHeight);
+  };
+
+  // "View Manager"
+  var currentView = 'text';
+  var views = {
+      'text': htmlView,
+      'canvas': canvasView
+  };
+  var draw = function() {
+      views[currentView].pf = pf;
+      views[currentView].draw();
+  };
+
+  var selectDepiction = document.getElementById('select_depiction');
+  selectDepiction.onchange = function() {
+      var value = selectDepiction.options[selectDepiction.selectedIndex].value;
+      if (value === 'text') {
+          displayText.style.display = 'block';
+          displayCanvas.style.display = 'none';
+      } else {
+          displayText.style.display = 'none';
+          displayCanvas.style.display = 'block';
+      }
+      currentView = value;
+      draw();
+  };
+
+  // Controller.  We don't subclass, we just monkeypatch.
   var c = new yoob.Controller();
-  var pf;
-  
+
   c.load = function(text) {
       pf = new yoob.Playfield();
       pf.setDefault('Space');
       pf.load(0, 0, text, loadMapper);
-      output.innerHTML = pf.dump(dumpMapper);
+      draw();
   };
 
   c.step = function() {
       newPf.setDefault('Space');
       evolve_playfield(pf, newPf);
       pf = newPf;
-      output.innerHTML = pf.dump(dumpMapper);
+      draw();
   };
 
   c.connect({
     'edit': 'edit',
     'speed': 'speed',
     'source': 'input',
-    'display': 'output'
+    'display': 'display_container'
   });
+  
   c.click_load();
 </script>

demo/yoob/controller.js

 /*
- * This file is part of yoob.js version 0.3-PRE
+ * This file is part of yoob.js version 0.3
+ * Available from https://github.com/catseye/yoob.js/
  * This file is in the public domain.  See http://unlicense.org/ for details.
  */
 if (window.yoob === undefined) yoob = {};

demo/yoob/playfield-canvas-view.js

+/*
+ * This file is part of yoob.js version 0.5
+ * Available from https://github.com/catseye/yoob.js/
+ * This file is in the public domain.  See http://unlicense.org/ for details.
+ */
+if (window.yoob === undefined) yoob = {};
+
+/*
+ * A view (in the MVC sense) for depicting a yoob.Playfield (-compatible)
+ * object on an HTML5 <canvas> element (or compatible object).
+ *
+ * TODO: don't necesarily resize canvas each time?
+ * TODO: option to stretch content rendering to fill a fixed-size canvas
+ */
+yoob.PlayfieldCanvasView = function() {
+    this.pf = undefined;
+    this.canvas = undefined;
+
+    this.init = function(pf, canvas) {
+        this.pf = pf;
+        this.canvas = canvas;
+        this.cursors = [];
+        this.cellWidth = 8;
+        this.cellHeight = 8;
+        return this;
+    };
+    
+    /* Chain setters */
+    this.setCursors = function(cursors) {
+        this.cursors = cursors;
+        return this;
+    };
+    this.setCellDimensions = function(cellWidth, cellHeight) {
+        this.cellWidth = cellWidth;
+        this.cellHeight = cellHeight;
+        return this;
+    };
+
+    /*
+     * Override these if you want to draw some portion of the
+     * playfield which is not the whole playfield.
+     * (Not yet implemented)
+     */
+    this.getLowerX = function() {
+        return this.pf.getMinX();
+    };
+    this.getUpperX = function() {
+        return this.pf.getMaxX();
+    };
+    this.getLowerY = function() {
+        return this.pf.getMinY();
+    };
+    this.getUpperY = function() {
+        return this.pf.getMaxY();
+    };
+
+    /*
+     * Returns the number of occupied cells in the x direction.
+     */
+    this.getExtentX = function() {
+        if (this.getLowerX() === undefined || this.getUpperX() === undefined) {
+            return 0;
+        } else {
+            return this.getUpperX() - this.getLowerX() + 1;
+        }
+    };
+
+    /*
+     * Returns the number of occupied cells in the y direction.
+     */
+    this.getExtentY = function() {
+        if (this.getLowerY() === undefined || this.getUpperY() === undefined) {
+            return 0;
+        } else {
+            return this.getUpperY() - this.getLowerY() + 1;
+        }
+    };
+
+    /*
+     * Draws cells of the Playfield in a drawing context.
+     * cellWidth and cellHeight are canvas units of measure.
+     *
+     * The default implementation tries to call a .draw() method on the cell's
+     * value, if one exists, and just renders it as text, in black, if not.
+     *
+     * Override if you wish to draw elements in some other way.
+     */
+    this.drawCell = function(ctx, value, playfieldX, playfieldY,
+                             canvasX, canvasY, cellWidth, cellHeight) {
+        if (value.draw !== undefined) {
+            value.draw(ctx, playfieldX, playfieldY, canvasX, canvasY,
+                       cellWidth, cellHeight);
+        } else {
+            ctx.fillStyle = "black";
+            ctx.fillText(value.toString(), canvasX, canvasY);
+        }
+    };
+
+    /*
+     * Draws the Playfield in a drawing context.
+     * cellWidth and cellHeight are canvas units of measure for each cell.
+     * offsetX and offsetY are canvas units of measure for the top-left
+     *   of the entire playfield.
+     */
+    this.drawContext = function(ctx, offsetX, offsetY, cellWidth, cellHeight) {
+        var self = this;
+        this.pf.foreach(function (x, y, value) {
+            self.drawCell(ctx, value, x, y,
+                          offsetX + x * cellWidth, offsetY + y * cellHeight,
+                          cellWidth, cellHeight);
+        });
+    };
+
+    /*
+     * Draws the Playfield, and a set of Cursors, on a canvas element.
+     * Resizes the canvas to the needed dimensions.
+     * cellWidth and cellHeight are canvas units of measure for each cell.
+     * Note that this is a holdover from when this method was on Playfield
+     * itself; typically you'd just call draw() instead.
+     */
+    this.drawCanvas = function(canvas, cellWidth, cellHeight, cursors) {
+        var ctx = canvas.getContext('2d');
+      
+        var width = this.getExtentX();
+        var height = this.getExtentY();
+
+        if (cellWidth === undefined) {
+            ctx.textBaseline = "top";
+            ctx.font = cellHeight + "px monospace";
+            cellWidth = ctx.measureText("@").width;
+        }
+
+        canvas.width = width * cellWidth;
+        canvas.height = height * cellHeight;
+
+        ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+        ctx.textBaseline = "top";
+        ctx.font = cellHeight + "px monospace";
+
+        var offsetX = this.pf.getMinX() * cellWidth * -1;
+        var offsetY = this.pf.getMinY() * cellHeight * -1;
+
+        if (this.fixedPosition) {
+            offsetX = 0;
+            offsetY = 0;
+        }
+
+        for (var i = 0; i < cursors.length; i++) {
+            cursors[i].drawContext(
+              ctx,
+              offsetX + cursors[i].x * cellWidth,
+              offsetY + cursors[i].y * cellHeight,
+              cellWidth, cellHeight
+            );
+        }
+
+        this.drawContext(ctx, offsetX, offsetY, cellWidth, cellHeight);
+    };
+
+    /*
+     * Render the playfield on the canvas.
+     */
+    this.draw = function() {
+        this.drawCanvas(
+          this.canvas, this.cellWidth, this.cellHeight, this.cursors
+        );
+    };
+
+};

demo/yoob/playfield-html-view.js

+/*
+ * This file is part of yoob.js version 0.5
+ * Available from https://github.com/catseye/yoob.js/
+ * This file is in the public domain.  See http://unlicense.org/ for details.
+ */
+if (window.yoob === undefined) yoob = {};
+
+/*
+ * A view (in the MVC sense) for depicting a yoob.Playfield (-compatible)
+ * object onto any DOM element that supports innerHTML.
+ *
+ * TODO: this may be incomplete; use at your own risk
+ * TODO: have this and the canvas view inherit from a common ABC?
+ */
+yoob.PlayfieldHTMLView = function() {
+    this.pf = undefined;
+    this.element = undefined;
+
+    this.init = function(pf, element) {
+        this.pf = pf;
+        this.element = element;
+        return this;
+    };
+
+    /*
+     * Override these if you want to draw some portion of the
+     * playfield which is not the whole playfield.
+     */
+    this.getLowerX = function() {
+        return this.pf.getMinX();
+    };
+    this.getUpperX = function() {
+        return this.pf.getMaxX();
+    };
+    this.getLowerY = function() {
+        return this.pf.getMinY();
+    };
+    this.getUpperY = function() {
+        return this.pf.getMaxY();
+    };
+
+    /*
+     * Returns the number of occupied cells in the x direction.
+     */
+    this.getExtentX = function() {
+        if (this.getLowerX() === undefined || this.getUpperX() === undefined) {
+            return 0;
+        } else {
+            return this.getUpperX() - this.getLowerX() + 1;
+        }
+    };
+
+    /*
+     * Returns the number of occupied cells in the y direction.
+     */
+    this.getExtentY = function() {
+        if (this.getLowerY() === undefined || this.getUpperY() === undefined) {
+            return 0;
+        } else {
+            return this.getUpperY() - this.getLowerY() + 1;
+        }
+    };
+
+    /*
+     * Override to convert Playfield values to HTML.
+     */
+    this.render = function(value) {
+        return value;
+    };
+
+    /*
+     * Render the playfield, as HTML, on the DOM element.
+     */
+    this.draw = function() {
+        var text = "";
+        for (var y = this.getLowerY(); y <= this.getUpperY(); y++) {
+            var row = "";
+            for (var x = this.getLowerX(); x <= this.getUpperX(); x++) {
+                row += this.render(this.pf.get(x, y));
+            }
+            text += row + "\n";
+        }
+        this.element.innerHTML = text;
+    };
+
+};

demo/yoob/playfield.js

 /*
- * This file is part of yoob.js version 0.2
+ * This file is part of yoob.js version 0.4
+ * Available from https://github.com/catseye/yoob.js/
  * This file is in the public domain.  See http://unlicense.org/ for details.
  */
 if (window.yoob === undefined) yoob = {};
      */
     this.setDefault = function(v) {
         this._default = v;
+        return this;
     };
 
     /*
     };
 
     /*
+     * Scroll a rectangular subrectangle of this Playfield, up.
+     * TODO: support other directions.
+     */
+    this.scrollRectangleY = function(dy, minX, minY, maxX, maxY) {
+        if (dy < 1) {
+            for (var y = minY; y <= (maxY + dy); y++) {
+                for (var x = minX; x <= maxX; x++) {
+                    this.put(x, y, this.get(x, y - dy));
+                }
+            }
+        } else { alert("scrollRectangleY(" + dy + ") notImplemented"); }
+    };
+
+    this.clearRectangle = function(minX, minY, maxX, maxY) {
+        // Could also do this with a foreach that checks
+        // each position.  Would be faster on sparser playfields.
+        for (var y = minY; y <= maxY; y++) {
+            for (var x = minX; x <= maxX; x++) {
+                this.put(x, y, undefined);
+            }
+        }
+    };
+
+    /*
      * Load a string into this Playfield.
      * The string may be multiline, with newline (ASCII 10)
      * characters delimiting lines.  ASCII 13 is ignored.
     };
 
     /*
-     * Draws elements of the Playfield in a drawing context.
-     * x and y are canvas coordinates, and width and height
-     * are canvas units of measure.
-     * The default implementation just renders them as text,
-     * in black.
-     * Override if you wish to draw them differently.
+     * Accessors for the minimum (resp. maximum) x (resp. y) values of
+     * occupied (non-default-valued) cells in this Playfield.  If there are
+     * no cells in this Playfield, these will refturn undefined.  Note that
+     * these are not guaranteed to be tight bounds; if values in cells
+     * are deleted, these bounds may still be considered to be outside them.
      */
-    this.drawElement = function(ctx, x, y, cellWidth, cellHeight, elem) {
-        ctx.fillStyle = "black";
-        ctx.fillText(elem.toString(), x, y);
+    this.getMinX = function() {
+        return this.minX;
+    };
+    this.getMaxX = function() {
+        return this.maxX;
+    };
+    this.getMinY = function() {
+        return this.minY;
+    };
+    this.getMaxY = function() {
+        return this.maxY;
     };
 
     /*
-     * Draws the Playfield in a drawing context.
-     * cellWidth and cellHeight are canvas units of measure for each cell.
+     * Returns the number of occupied cells in the x direction.
      */
-    this.drawContext = function(ctx, offsetX, offsetY, cellWidth, cellHeight) {
-        var me = this;
-        this.foreach(function (x, y, elem) {
-            me.drawElement(ctx, offsetX + x * cellWidth, offsetY + y * cellHeight,
-                           cellWidth, cellHeight, elem);
-        });
-    };
-
     this.getExtentX = function() {
         if (this.maxX === undefined || this.minX === undefined) {
             return 0;
         }
     };
 
+    /*
+     * Returns the number of occupied cells in the y direction.
+     */
     this.getExtentY = function() {
         if (this.maxY === undefined || this.minY === undefined) {
             return 0;
             return this.maxY - this.minY + 1;
         }
     };
-
-    /*
-     * Draws the Playfield, and a set of Cursors, on a canvas element.
-     * Resizes the canvas to the needed dimensions.
-     * cellWidth and cellHeight are canvas units of measure for each cell.
-     */
-    this.drawCanvas = function(canvas, cellWidth, cellHeight, cursors) {
-        var ctx = canvas.getContext('2d');
-      
-        var width = this.getExtentX();
-        var height = this.getExtentY();
-
-        if (cellWidth === undefined) {
-            ctx.textBaseline = "top";
-            ctx.font = cellHeight + "px monospace";
-            cellWidth = ctx.measureText("@").width;
-        }
-
-        canvas.width = width * cellWidth;
-        canvas.height = height * cellHeight;
-
-        ctx.clearRect(0, 0, canvas.width, canvas.height);
-
-        ctx.textBaseline = "top";
-        ctx.font = cellHeight + "px monospace";
-
-        var offsetX = this.minX * cellWidth * -1;
-        var offsetY = this.minY * cellHeight * -1;
-
-        for (var i = 0; i < cursors.length; i++) {
-            cursors[i].drawContext(
-              ctx,
-              cursors[i].x * cellWidth, cursors[i].y * cellHeight,
-              cellWidth, cellHeight
-            );
-        }
-
-        this.drawContext(ctx, offsetX, offsetY, cellWidth, cellHeight);
-    };
-
 };