GUI for Xholon grid apps using HTML5 Canvas

Ken Webb 2011-01-27T14:13:25Z

The Bestiary sample web app currently uses PulpCore graphics. Using the scripts on this page, the Bestiary web app can also use the HTML5 Canvas.

The first script is a HTML5 Canvas server. It's written in JavaScript and runs in a web browser such as Firefox. Copy the script from this wiki page, and paste it into the console in the Bestiary app. Then press the submit button.

<script implName="lang:BrowserJS:inline:"><![CDATA[
var drawScene_Function = "\
<script type='text/javascript'>\
/*\
 * drawScene(context, dataFromClient)\
 * id: 10=Cat 11=Human 3=Dog 4=Zombie 5=Termite\
 * example of data: id,x,y\
 * 1,13,20|\
 * 1,16,31|\
 * 2,17,15|\
 * 3,17,16|\
 * 1,17,21\
 */\
function drawScene(ctx, theData) {\
  ctx.fillStyle = 'green';\
  ctx.fillRect(0, 0, 499, 499);\
  var rowItems = theData.split('|');\
  for (i in rowItems) {\
    var colItems = rowItems[i].split(',');\
    if (colItems.length != 3) {continue;}\
    var id = colItems[0];\
    var x = colItems[1] * 5;\
    var y = colItems[2] * 5;\
    /* customized for Bestiary */\
    switch (id) {\
    case '11': ctx.fillStyle = 'tan'; break;\
    case '10': ctx.fillStyle = 'lightgrey'; break;\
    case '3': ctx.fillStyle = 'white'; break;\
    case '4': ctx.fillStyle = 'pink'; break;\
    case '5': ctx.fillStyle = 'yellow'; break;\
    default: ctx.fillStyle = 'magenta'; break;\
    }\
    ctx.fillRect(x, y, 5, 5);\
  }\
};\
</script>";
$('div#divHeadOne').html(drawScene_Function);
$('div#divOne').html("<canvas id='graphicsServerCanvas' width='500' height='500'></canvas>");
]]></script>

The second script is a client of the HTML5 Canvas server. It's written in the BeanShell variant of Java, which allows it to be dynamically added to a running app. Copy the script from this wiki page, and paste it into the console in the Bestiary app. Then press the submit button.

<AlternativeGui implName="org.primordion.xholon.base.XholonNull">

<HTML5Canvas implName="org.primordion.script.Behavior_beanshell"><![CDATA[
import org.primordion.xholon.app.Application;
import org.primordion.xholon.base.AbstractGrid;
import org.primordion.xholon.base.IGrid;
import org.primordion.xholon.base.IXholon;
import org.primordion.xholon.base.IXholonClass;
import org.primordion.xholon.service.IScriptService;
import org.primordion.xholon.service.IXholonService;

behavior() {

/** The current context node. */
//private IXholon context = null;
/** The xholon that owns the Row and GridCell xholons. */
private IXholon gridOwner = null;
/** The Xholon application. */
private Application app = null;
/** The xholon at upper left corner of the grid. */
private AbstractGrid upperLeft = null;
/** Default size of a grid cell, in pixels. */
private int cellSize = 5;
private int nRows = 0;
private int nCols = 0;
/** A service that knows how to evaluate BrowserJS and other scripts. */
private IScriptService scriptService = null;

public void postConfigure()
{
  app = applicationKey;
  scriptService = (IScriptService)app.getService(IXholonService.XHSRV_SCRIPT);
  nRows = 100; //Stage.getHeight() / cellSize;
  nCols = 100; //Stage.getWidth() / cellSize;
  gridOwner = app.getRoot().getFirstChild().getNextSibling();
  setUpperLeft();
  writeGuiToCanvas();
}

public void act() {
  writeGuiToCanvas();
}

protected void writeGuiToCanvas() {
  String data = drawGrid();
  String scriptContent = createBrowserScript(data);
  scriptService.evalScript(contextNodeKey, "BrowserJS", scriptContent);
}

/**
 * Draw the grid to a String.
 * @return 
 */
protected String drawGrid() {
  StringBuffer sb = new StringBuffer();
  AbstractGrid currentCell = upperLeft;
  AbstractGrid startOfRow = upperLeft;
  for (int i = 0; i < nRows; i++) {
    //System.out.println("row:" + i);
    for (int j = 0; j < nCols; j++) {
      if (currentCell.hasChildNodes()) {
        sb.append(currentCell.getLastChild().getXhcId()).append(",").append(j).append(",").append(i).append("|");
      }
      currentCell = (AbstractGrid)currentCell.port[1];
    }
    startOfRow = (AbstractGrid)startOfRow.port[2];
    currentCell = startOfRow;
  }
  return sb.toString();
}

/**
 * Create a BrowserJS script.
 * @param data
 * @return 
 */
protected String createBrowserScript(String data) {
  StringBuffer sb = new StringBuffer()
  .append("var myData = '").append(data).append("';")
  .append("var myCanvas = document.getElementById('graphicsServerCanvas');")
  .append("var ctx = myCanvas.getContext('2d');")
  .append("drawScene(ctx, myData);");
  return sb.toString();
}

/**
 * Set the xholon at the upper left corner of the grid.
 */
protected void setUpperLeft() {
  AbstractGrid row = (AbstractGrid)gridOwner.getFirstChild();
  AbstractGrid col = null;
  while (row != null) {
    if (row.getXhcName().equals("Row")
        || row.getXhType() == IXholonClass.XhtypeGridEntity
        || row.getRoleName().equals("row")) { // first child of type "Row"
      col = (AbstractGrid)row.getFirstChild();
      while (col != null) {
        if (col.getXhcName().equals("GridCell")
            || col.getXhType() == IXholonClass.XhtypeGridEntityActivePassive
            || col.getRoleName().equals("gridcell")) { // first child of type "GridCell"
          upperLeft = col; // found it
          return;
        }
        col = (AbstractGrid)col.getNextSibling();
      }
      return;
    }
    row = (AbstractGrid)row.getNextSibling();
  }
}

return this;
}
behaviorObject = behavior();
]]></HTML5Canvas>

</AlternativeGui>

With both GUIs running at the same time, the app is quite slow. It's a bit faster if you pause the PuplCore graphics. Click the mouse inside the PulpCore GUI (this is the original GUI on the page). Then press the P key on your keyboard, to toggle the PulpCore graphics off and on.

Internet Explorer

If you are using Internet Explorer (tested with IE 7 and 8), then the first script above cannot have any end-of-line characters in it. Here's a packed version of the first script.

<script implName="lang:BrowserJS:inline:"><![CDATA[var drawScene_Function="<script type='text/javascript'>function drawScene(ctx, theData) {  ctx.fillStyle = 'green';  ctx.fillRect(0, 0, 499, 499);  var rowItems = theData.split('|');  for (i in rowItems) {    var colItems = rowItems[i].split(',');    if (colItems.length != 3) {continue;}    var id = colItems[0];    var x = colItems[1] * 5;    var y = colItems[2] * 5;    switch (id) {    case '11': ctx.fillStyle = 'tan'; break;    case '10': ctx.fillStyle = 'lightgrey'; break;    case '3': ctx.fillStyle = 'white'; break;    case '4': ctx.fillStyle = 'pink'; break;    case '5': ctx.fillStyle = 'yellow'; break;    default: ctx.fillStyle = 'magenta'; break;    }    ctx.fillRect(x, y, 5, 5);  }};</script>";$('div#divHeadOne').html(drawScene_Function);$('div#divOne').html("<canvas id='graphicsServerCanvas' width='500' height='500'></canvas>");if(jQuery.browser.msie){var myEl=document.getElementById('graphicsServerCanvas');G_vmlCanvasManager.initElement(myEl)}]]></script>

New version with compiled Java client

server

<script implName="lang:BrowserJS:inline:"><![CDATA[
var drawScene_Function = "\
<script type='text/javascript'>\
/*\
 * drawScene(context, dataFromClient)\
 * id: 10=Cat 11=Human ?=Dog ?=Zombie ?=Termite\
 * 15=Door 16=WallSection 17=HouseInteriorSection 18=Porch 19=Entrance\
 * example of data: id,x,y\
 * 1,13,20|\
 * 1,16,31|\
 * 2,17,15|\
 * 3,17,16|\
 * 1,17,21\
 */\
function drawScene(ctx, theData) {\
  ctx.fillStyle = 'green';\
  ctx.fillRect(0, 0, 499, 499);\
  var rowItems = theData.split('|');\
  for (i in rowItems) {\
    var colItems = rowItems[i].split(',');\
    if (colItems.length != 3) {continue;}\
    var id = colItems[0];\
    var x = colItems[1] * 5;\
    var y = colItems[2] * 5;\
    /* customized for Bestiary */\
    switch (id) {\
    case 'Cat': ctx.fillStyle = 'lightgrey'; break;\
    case 'Hum': ctx.fillStyle = 'tan'; break;\
    case 'Doo': ctx.fillStyle = 'lightgreen'; break;\
    case 'Wal': ctx.fillStyle = 'orange'; break;\
    case 'Hou': ctx.fillStyle = 'black'; break;\
    case 'Por': ctx.fillStyle = 'lightgreen'; break;\
    case 'Ent': ctx.fillStyle = 'lightgreen'; break;\
    case 'Dog': ctx.fillStyle = 'white'; break;\
    case 'Zom': ctx.fillStyle = 'pink'; break;\
    case 'Ter': ctx.fillStyle = 'yellow'; break;\
    default: ctx.fillStyle = 'magenta'; break;\
    }\
    ctx.fillRect(x, y, 5, 5);\
  }\
};\
</script>";
$('div#divHeadOne').html(drawScene_Function);
$('div#divOne').html("<canvas id='graphicsServerCanvas' width='500' height='500'></canvas>");
if(jQuery.browser.msie) {
    var myEl = document.getElementById('graphicsServerCanvas');
    G_vmlCanvasManager.initElement(myEl);
}
]]></script>

use of the compiled client

<AlternativeGui implName="org.primordion.xholon.base.XholonNull">
  <GridGuiDataProvider implName="org.primordion.xholon.io.GridGuiDataProvider" idType="3" idTypeAbbrevLen="3">
  </GridGuiDataProvider>
</AlternativeGui>

client script for testing the server

<script implName="lang:BrowserJS:inline:"><![CDATA[
var myData = 'Cat,13,20|Cat,16,31|Dog,17,15|Wal,17,16|Cat,17,21|Zom,33,33|Ter,40,40';
var myCanvas = document.getElementById('graphicsServerCanvas');
var ctx = myCanvas.getContext('2d');
drawScene(ctx, myData);
]]></script>

return to main page