NetLogo Global Climate model and JavaScript grapher
This example displays a NetLogo model sending data to a JavaScript grapher
NetLogo Global Climate model and JavaScript grapher
This example displays a NetLogo model sending data to a JavaScript grapher
/* | |
* Minimal classList shim for IE 9 | |
* By Devon Govett | |
* MIT LICENSE | |
*/ | |
if (!("classList" in document.documentElement) && Object.defineProperty && typeof HTMLElement !== 'undefined') { | |
Object.defineProperty(HTMLElement.prototype, 'classList', { | |
get: function() { | |
var self = this; | |
function update(fn) { | |
return function(value) { | |
var classes = self.className.split(/\s+/), | |
index = classes.indexOf(value); | |
fn(classes, index, value); | |
self.className = classes.join(" "); | |
} | |
} | |
var ret = { | |
add: update(function(classes, index, value) { | |
~index || classes.push(value); | |
}), | |
remove: update(function(classes, index) { | |
~index && classes.splice(index, 1); | |
}), | |
toggle: update(function(classes, index, value) { | |
~index ? classes.splice(index, 1) : classes.push(value); | |
}), | |
contains: function(value) { | |
return !!~self.className.split(/\s+/).indexOf(value); | |
}, | |
item: function(i) { | |
return self.className.split(/\s+/)[i] || null; | |
} | |
}; | |
Object.defineProperty(ret, 'length', { | |
get: function() { | |
return self.className.split(/\s+/).length; | |
} | |
}); | |
return ret; | |
} | |
}); | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>NetLogo Applet</title> | |
<link href="styles.css" rel="stylesheet" type="text/css"> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> | |
<script src="http://mbostock.github.com/d3/d3.v2.js?2.8.1"></script> | |
<script src="classlist-shim-for-ie.js" type="text/javascript"></script> | |
<script src="https://raw.github.com/gist/2764196/610b262e9b266a6f71c58e0d2ad27a304475466c/dgApi.js" type="text/javascript"></script> | |
</head> | |
<body> | |
<h1>Simple Global Climate Change NetLogo Applet</h1> | |
<ul class="hlist"> | |
<li> | |
<div id="appletwrapper"> | |
<ul class="hlist"> | |
<li><button id="run-button" class="nlogo">Run</button></li> | |
<li><button id="reset-button" class="nlogo">Reset</button></li> | |
<li><button id="watch-sunray-button" class="nlogo">Watch Sunray</button></li> | |
</ul> | |
<applet id="applet" code="org.nlogo.lite.Applet" archive="http://stepheneb.github.com/netlogo-gcc/NetLogoLite.jar" width="590" height="430"> | |
<param name="DefaultModel" value="http://stepheneb.github.com/netlogo-gcc/GCCModel.v3.nlogo"> | |
</applet> | |
</div> | |
</li> | |
<li> | |
<div id="chart" class="chart"></div> | |
</li> | |
</ul> | |
<script src="netlogo.js" type="text/javascript"></script> | |
</body> | |
</html> |
/*global browser:true */ | |
var applet = document.getElementById("applet"), | |
run_button = document.getElementById("run-button"), | |
reset_button = document.getElementById("reset-button"), | |
watch_sunray_button = document.getElementById("watch-sunray-button"), | |
nl_obj_panel, // org.nlogo.lite.Applet object | |
nl_obj_workspace, // org.nlogo.lite.LiteWorkspace | |
nl_obj_world, // org.nlogo.agent.World | |
nl_obj_program, // org.nlogo.api.Program | |
nl_obj_state, | |
nl_obj_observer, | |
nl_obj_globals, | |
sw, | |
pw, | |
nlogo_elements, | |
globals = [], i, | |
data_array = [], | |
graph; | |
window.onload=function() { | |
disable_nlogo_elements(); | |
dgApi.initGame("NetLogo Data", [ { name: "ticks", type: 'numeric', description: "Current tick", precision: 0 }, | |
{ name: "temperature", type: 'numeric', description: "Global Climate Temperature", precision: 2 } | |
]); | |
dgApi.beginRun(); | |
// | |
// NetLogo Applet Loading Handler | |
// | |
// Wait until the applet is loaded and initialized before enabling buttons | |
// and creating JavaScript variables for Java objects in the applet. | |
// | |
applet.ready = false; | |
applet.checked_more_than_once = false; | |
window.setTimeout (function() { isAppletReady(); }, 250); | |
function isAppletReady() { | |
try { | |
applet.ready = applet.panel(); | |
} catch (e) { | |
// Do nothing--we'll try again in the next timer interval. | |
} | |
if(applet.ready) { | |
nl_setup_objects(); | |
nl_obj_panel.commandLater("set done true"); | |
sw = new applet.Packages.java.io.StringWriter(); | |
pw = new applet.Packages.java.io.PrintWriter(sw); | |
enable_nlogo_elements(); | |
if(applet.checked_more_than_once) { | |
clearInterval(applet.checked_more_than_once); | |
applet.checked_more_than_once = false; | |
} | |
} else { | |
if(!applet.checked_more_than_once) { | |
applet.checked_more_than_once = window.setInterval(function() { isAppletReady(); }, 250); | |
} | |
} | |
} | |
// | |
// Create these JavaScript objects to provide access to the | |
// corresponding Java objects in NetLogo. | |
// | |
function nl_setup_objects() { | |
nl_obj_panel = applet.panel(); | |
nl_obj_workspace = nl_obj_panel.workspace(); | |
nl_obj_world = nl_obj_workspace.org$nlogo$lite$LiteWorkspace$$world; | |
nl_obj_program = nl_obj_world.program(); | |
nl_obj_observer = nl_obj_world.observer(); | |
nl_obj_globals = nl_obj_program.globals(); | |
} | |
// | |
// NetLogo command interface | |
// | |
function nl_cmd_start() { | |
nl_obj_panel.commandLater("set done false while [not done] [ execute ]"); | |
} | |
function nl_cmd_stop() { | |
nl_obj_panel.commandLater("set done true"); | |
} | |
function nl_cmd_execute(cmd) { | |
nl_obj_panel.commandLater(cmd); | |
} | |
function nl_cmd_save_state() { | |
nl_obj_world.exportWorld(pw, true); | |
nl_obj_state = sw.toString(); | |
} | |
function nl_cmd_restore_state() { | |
if (nl_obj_state) { | |
var sr = new applet.Packages.java.io.StringReader(nl_obj_state); | |
nl_obj_workspace.importWorld(sr); | |
nl_obj_panel.commandLater("display"); | |
} | |
} | |
function nl_cmd_reset() { | |
nl_cmd_stop(); | |
nl_obj_panel.commandLater("startup"); | |
} | |
// | |
// Managing the NetLogo data polling system | |
// | |
function startNLDataPoller() { | |
applet.data_poller = window.setInterval(function() { nlDataPoller(); }, 200); | |
} | |
function stopNLDataPoller() { | |
if (applet.data_poller) { | |
clearInterval(applet.data_poller); | |
applet.data_poller = false; | |
} | |
} | |
function nlDataPoller() { | |
data_array.push(nl_obj_observer.getVariable(3)); | |
dgApi.addTick([ data_array.length, data_array[data_array.length-1] ]); | |
} | |
// | |
// button handlers | |
// | |
run_button.onclick = function() { | |
if (run_button.textContent == "Run") { | |
run_button_run(); | |
} else { | |
run_button_stop(); | |
} | |
}; | |
reset_button.onclick = function() { | |
run_button_stop(); | |
nl_cmd_reset(); | |
data_array.length = 0; | |
dgApi.endRun(); | |
dgApi.beginRun(); | |
}; | |
watch_sunray_button.onclick = function() { | |
nl_cmd_execute("watch one-of sunrays with [ycor > (max-pycor / 2 ) and heading > 90 ]"); | |
}; | |
// | |
// button/view helpers | |
// | |
function run_button_run() { | |
nl_cmd_start(); | |
startNLDataPoller(); | |
run_button.textContent = "Stop"; | |
} | |
function run_button_stop() { | |
nl_cmd_stop(); | |
stopNLDataPoller(); | |
run_button.textContent = "Run"; | |
} | |
// | |
// add the css class "inactive" to all dom elements that include the class "nlogo" | |
// | |
function disable_nlogo_elements() { | |
nlogo_elements = document.getElementsByClassName("nlogo"); | |
for (i=0; i < nlogo_elements.length; i++) { | |
nlogo_elements[i].classList.add("inactive"); | |
} | |
} | |
// | |
// add the css class "active" to all dom elements that include the class "nlogo" | |
// | |
function enable_nlogo_elements() { | |
nlogo_elements = document.getElementsByClassName("nlogo"); | |
for (i=0; i < nlogo_elements.length; i++) { | |
nlogo_elements[i].classList.remove("inactive"); | |
nlogo_elements[i].classList.add("active"); | |
} | |
} | |
}; |
body { font: 12px Verdana, Arial, Helvetica, sans-serif; | |
margin: 1.0em 2.0em; | |
background-color: white;} | |
h1 { font-size: 1.6em; | |
font-weight: bold; } | |
h2 { font-size: 1.4em; | |
font-weight: bold; } | |
h1 { font-size: 1.2em; | |
font-weight: bold; } | |
hr { margin: 2em 0em; } | |
p { max-width: 600px; } | |
#appletwrapper { border: 1px solid white; | |
padding: 10px; | |
width: 600px; | |
height: 490px; | |
background-color: white; } | |
applet { padding: 0px; | |
background-color: white; } | |
table { border: 1px solid gray; | |
border-spacing: 0px; | |
border-collapse: collapse; | |
font: 10px/24px Verdana, Arial, Helvetica, sans-serif; } | |
table th { | |
border: 1px solid gray; | |
padding: 0em 1em; | |
text-align: center; } | |
table td { | |
font-size: 0.9em; | |
border: 1px solid gray; | |
padding: 0em 1em; | |
text-align: right; | |
width: 14em; } | |
table td.left { | |
padding: 0em 1em 0em 2em; | |
text-align: left; } | |
ul { | |
list-style-type: none; | |
padding: 0em 0em; | |
margin: 0.5em 0em 0em 0.5em; | |
width: 100%; } | |
ul li { | |
margin: 0em; | |
padding: 0em 1em; } | |
ul.hlist li { | |
display: table-cell; | |
vertical-align: top; | |
margin: 0em; | |
padding: 0em 0.5em; } | |
button.active { | |
font-weight: bold; | |
font-color: black; | |
border-bottom: 2px solid black; } | |
button { | |
font-weight: normal; | |
font-color: gray; | |
border-bottom: 0px solid black; } | |
pre { font-size: 0.8em; | |
border: 1px solid gray; | |
padding: 1em; | |
background-color: #F0F0F0; | |
width: 80em; | |
height: 40em; | |
overflow: scroll; } | |
.chart { | |
border: 1px solid white; | |
background-color: #FAFAFA; | |
margin-top: 20px; | |
width: 580px; | |
height: 465px; } | |
circle, .line { | |
fill: none; | |
stroke: steelblue; | |
stroke-width: 1px; } | |
text.title { font-size: 1.2em; | |
font-weight: bold; } | |
text.axis { font-size: 1.0em; | |
font-weight: normal; } | |
circle { | |
fill: white; | |
fill-opacity: 0.2; | |
cursor: move; } | |
circle.selected { | |
fill: #ff7f0e; | |
stroke: #ff7f0e; } | |
circle:hover { | |
fill: #ff7f0e; | |
stroke: #707f0e; } | |
circle.selected:hover { | |
fill: #ff7f0e; | |
stroke: #ff7f0e; } |