NetLogo / Tortoise

Compiler and runtime engine for NetLogo models that runs in JavaScript 🐢
https://netlogoweb.org
Other
56 stars 27 forks source link

Can I use Tortoise individually without the NetLogo Web front-end? #206

Closed CIVITAS-John closed 5 years ago

CIVITAS-John commented 5 years ago

Dear All,

I am relatively new to the NetLogo community, and is interested in migrating NetLogo into the mobile end (for educational propose). I wonder if it is possible now to directly use the 'engine' and 'compiler' part as a standalone Javascript API, thus enabling me to further link it into another rendering engine through an embedded Javascript runtime.

May I ask if it is instantly possible or what work should I do to approach it?

Best, John

TheBizzle commented 5 years ago

Yes, NetLogo Web was definitely designed with modularity in mind, and what you're describing is entirely possible. That said, depending on how much of the NetLogo Web GUI you wish to replace, you may find that this is not a very simple task, as the NetLogo language has a lot of coupling with the GUI (e.g. NetLogo expects there are global variables that are declared in the UI and can be modified both through the UI and the NetLogo language; numerous language primitives exist solely for showing dialog boxes, reading files, writing files, modifying plots, or otherwise performing I/O).

First, you will need to get ahold of the engine and the compiler. The engine is tortoise-engine.js and the compiler is tortoise-compiler.js. There are a number of ways you can get these files. The way that we do it in Galapagos is to declare a dependency in SBT, and then access them through WebJars. Most likely, though, you are not familiar with SBT/WebJars. More simply, you can download a netlogowebjs.jar from here (click a version on the right, then go to the "Files" tab), open it up, and take the engine and compiler files from the root of the archive.

Should you want to use a customized version or Tortoise, or otherwise build Tortoise from scratch, clone the repo, set up GraalVM, run git submodule update --init to initialize the Model's Library, and then run ./sbt.sh netLogoWeb/test:fast and wait a few minutes while it builds Tortoise and runs some basic tests. After that, you'll be able to find the engine at engine/target/classes/js/tortoise-engine.js and the compiler at compiler/js/target/tortoise-compiler.js.

On a webpage with those two files loaded, you'll need to load up a NetLogo model. To do this, call (new BrowserCompiler()).fromNlogo(nlogo, []), where nlogo is the text contents of .nlogo file. To do additional compilations (such as recompiling code, running procedures, checking reporters, or making your own Command Center), refer to this code. These calls to BrowserCompiler return raw JavaScript code, which you should then eval.

If you want to manipulate the engine directly, without using the NetLogo language as an intermediate, there is no good API for that. The only way to learn the Tortoise engine "API" is basically to read the source code. See here, especially the engine package, and especially the engine/core package. If you want to know the JavaScript equivalent of a particular NetLogo primitive, check the compiler, namely SimplePrims and Prims.

Once you have the NetLogo engine doing your bidding, you'll need to get information out of it in order to draw a visualization. To access global variables, call world.observer.getGlobal(varName). To get view updates, first check Updater.hasUpdates(). If that value is true, then call Updater.collectUpdates(), which will return all visualization changes since the last call to collectUpdates().

Since these updates are time-relative, you might want some sort of data structure that is time-absolute (i.e. that represents all data that needs to be visualized, not only the things that recently changed). For that, we use the AgentModel class, which you can instantiate through calling new tortoise_require('agentmodel'). Store that into a variable (agentModel). With everything put together, it looks like:

initialize: ->
  this.agentModel = new tortoise_require('agentmodel')

loop: ->
  if Updater.hasUpdates()
    updates = Updater.collectUpdates()
    for update in updates
      this.agentModel.update(update)
    repaint()

If you want to run a model in a loop (like a standard NetLogo "go" button) with a "Speed" slider like NetLogo's, I suggest looking at this code, which handles frame-skipping.

Depending on what language primitives that you want to use, you may experience some runtime errors or primitives that do not seem to do anything. That is because Tortoise does not (and cannot) hold the proper implementations of all of the NetLogo primitives. Tortoise is designed to run headlessly (i.e. no GUI), and some primitives require a GUI (e.g. for popping up input boxes, or having the using select a file to upload, or to save a file). If you want to see how these "shims" are implemented in NetLogo Web, the relevant file is this one (though, it probably won't be very helpful to you, since most of this code is specific to the netlogoweb.org GUI). Alternatively (and also maybe not very helpfully), we do have shims that we provide when we run Tortoise headlessly in Nashorn/GraalVM (which is a Java Virtual Machine-based JS engine). You can see those here.

That should be most of what you need to know. If you have further questions, please direct them to the netlogo-devel mailing list, and Jeremy and I (and possibly others) can assist you there.

CIVITAS-John commented 5 years ago

Thank you Jason! That is an extremely clear explanation. Definitely, I will need to implement many things to replace the renderer & the UI. I will join the mailing list right now!