scottpdo / flocc

Agent-based modeling in JavaScript in the browser or on the server.
https://www.flocc.network
MIT License
28 stars 6 forks source link

Terrain.load can't be used from server side nodejs simulation #82

Open derickson opened 2 years ago

derickson commented 2 years ago

Describe the bug Terrain.load is dependent on the DOM const img = document.createElement("img"); so it won't work when running a server side model without a web browser.

To Reproduce Steps to reproduce the behavior:

  1. run the model headless in a nodejs app
  2. create a terrain and attempt to load a local image using the path, as described in function comments
  3. get error as document object not found

Expected behavior need example on how to use this from nodjs rather than browser. I'm using a terrain image to model a complex environment for an ABM

Desktop (please complete the following information): node v14.15.1 flocc v0.15.19

great project! thanks for all the work on this.

derickson commented 2 years ago

workaround: when the model is server side, use https://github.com/lukeapage/pngjs to load the PNG as data and copy the data to the terrain in the terrain init

var fs = require('fs'),
PNG = require('pngjs').PNG;

then the main. call back is necessary as the png io needs to finish before initializing the model

// Loads map resource from object in serial event async nodejs style
fs.createReadStream(model_params.map)
  .pipe(new PNG({filterType: 4}))
  .on("parsed", function () {
      //save the terrain image data into globalThis scope
      globalThis.terrainImage = this;

      // now start the simulation
      model.setup();
      run();
  });

then in the setup

  // load the terrain image to the Terrain object
  // works differently for browser vs nodejs
  if(globalThis.isBrowser) {
    modelStuff.terrain.load(model_params.map);
  } else {
    modelStuff.terrain.init((x,y) => {
      var idx = ((width * y) + x) << 2;
      var rv = globalThis.terrainImage.data[idx];
      var gv = globalThis.terrainImage.data[idx + 1];
      var bv = globalThis.terrainImage.data[idx + 2];
      modelStuff.terrain.set(x,y,{r:rv,g:gv,b:bv,a:255});
    });
  }
scottpdo commented 2 years ago

Hi @derickson pardon my slow reply. While Flocc does attempt to be equally useful in both server and browser contexts, the visualization components really are more browser-focused. It seems like your workaround using pngjs works nicely! One goal of Flocc is to be dependency-free, so I'd want to do a little more work on the Node side to remove that as a dependency, but it seems like that approach would work well.

derickson commented 2 years ago

no worries on slow reply, just trying to be a good open source community member. we are in the middle our our hackathon powered by flocc right now. really appreciate you making this project available.