Skip to content

Releases: scottpdo/flocc

0.4.3

31 Mar 15:03
Compare
Choose a tag to compare

One user-facing change: LineChartRenderer now displays horizontal tick marks to show time of the Environment.

Lots of testing changes, including using puppeteer to run headless Chromium to render tests (using pixel comparison for future changes).

0.4.2

26 Mar 18:31
Compare
Choose a tag to compare

A few bug fixes:

  • CanvasRenderer options: origin now should take a Point ({ x: number; y: number; }) in environment space, not screen space.
  • CanvasRenderer rendering: window.devicePixelRatio now respected on non-retina and retina screens for Network
  • Fix bug where environment.stat was caching multiple (different) calls to the same value

0.4.0

29 Feb 19:35
ee3ab17
Compare
Choose a tag to compare

Version 0.4.0 introduces the flocc.Terrain class! This is a new Environment helper that will likely displace GridEnvironments in the near future, as they're much more performant and scalable and produce much more compelling visualizations than the GridEnvironment with either an ASCIIRenderer or CanvasRenderer.

Terrain

A Terrain is a 2d grid that can act as background data for Agents in an Environment. Under the hood, data is stored in a Uint8ClampedArray, keeping values in the range 0-255, making it ideal for drawing to canvases. Like other helpers, a terrain should be instantiated and then added to its environment with environment.use, like this:

const [width, height] = 500, 500;
const environment = new Environment({ width, height });
const terrain = new Terrain(width, height);
environment.use(terrain);

Color Modes

Terrains can operate in either color (default) or grayscale modes. Grayscale mode can be switched on by instantiating a terrain with this configuration option:

const terrain = new Terrain(width, height, { grayscale: true });

Depending on if a terrain is in color or grayscale mode, coordinate values will look different. For color mode, a coordinate value is represented by a pixel-like object with r, g, b, and a keys (red, green, blue, and alpha/opacity, respectively). For example, if the coordinate at (100, 150) is completely red, it may look like this:

const pixel = terrain.sample(100, 150);
pixel.r; // 255 = full red
pixel.g; // 0 = no green
pixel.b; // 0 = no blue
pixel.a; // 255 = fully opaque (not transparent)

In grayscale mode, coordinate values are just numbers in the range 0-255. It is assumed that they are fully opaque (not transparent). If coordinate (10, 10) is white, (10, 15) is gray, and (10, 20) is black, the values will look like this:

const white = terrain.sample(10, 10); // 255
const gray = terrain.sample(10, 15); // 127
const black = terrain.sample(10, 20); // 0

Like Agents, a Terrain can have an update rule (a function) that is called once for every (x, y) coordinate in it (within the ranges x: 0 → width - 1 y: 0 → height - 1). Once the environment is using the terrain as a helper, the update rule is called with every environment.tick. There are two possible modes, depending if coordinates only modify themselves or if they modify others — synchronous and asynchronous, respectively.

Synchronous Mode

Terrains default to synchronous mode, where all coordinate updates happen simultaneously. In order to update coordinate values, the update rule has to return a new value — either a pixel-like object or a number, depending on the color mode. In synchronous mode, a coordinate can only update its own value (although it can do so based on neighboring coordinate values).

// turns every coordinate completely red if the x-value is
// greater than 200, completely blue if the x-value is less
// than 100, and leaves them unchanged in between
terrain.addRule((x, y) => {
    if (x > 200) {
        return {
            r: 255,
            g: 0, 
            b: 0,
            a: 255
        }
    } else if (x < 100) {
        return {
            r: 0,
            g: 0,
            b: 255,
            a: 255
        }
    }
});

Asynchronous mode

A terrain can be instantiated in async mode by passing an additional configuration parameter:

const terrain = new Terrain(width, height, { async: true });

In async mode, during an update rule, a coordinate may update not only its own value, but those of its neighbors. Async mode is slightly less performant (it runs slower), but may allow for more complex update rules.

terrain.addRule((x, y) => {
    // if the coordinate's red value is greater than 100,
    // increments its right neighbor's blue value
    if (terrain.sample(x, y).r > 100) {
        const neighborPixel = terrain.sample(x + 1, y);
        terrain.set(x + 1, y, {
            r: neighborPixel.r,
            g: neighborPixel.g,
            b: neighborPixel.b + 1,
            a: neighborPixel.a
        })
    }
});

Find full documentation on flocc.network!

0.3.17

19 Feb 10:48
Compare
Choose a tag to compare

A couple of under-the-hood improvements:

  • CanvasRenderer draw order when using a Network now draws all network connections first before drawing agents
  • Only distribute the dist directory when publishing to npm

0.3.15

09 Feb 20:54
f59c2fc
Compare
Choose a tag to compare

New options for CanvasRenderer (when visualizing connections on a Network)

  • connectionColor (default "black")
  • connectionOpacity (default 1)
  • connectionWidth (default 1)
    Also fixes visualization on retina devices.

Other under-the-hood improvements:

  • environment.stat filters out null values
  • LineChartRenderer and Histogram now use environment.stat to cache data from all agents

0.3.14

31 Jan 14:44
5c30c5a
Compare
Choose a tag to compare

Environment

Adds two new Environment methods: .stat and .memo.

  • environment.stat('key') returns an array of data associated with agent.get(key) across all agents in the environment. This is a shorthand for calling environment.getAgents().map(a => a.get(key)). It defaults to caching the result and using it for future calls within the same environment tick. However, this behavior can be turned off (for example, if agents are updating synchronously and the data should reflect this) by passing false as a second parameter: environment.stat('synchronousKey', false)
  • Calling environment.memo(function() { return 'someExpensiveValue' }) will return 'someExpensiveValue' and then cache and use that result for future calls within the same environment tick. The function that is passed as the only parameter is called only the first time .memo is used within an environment tick, and then its return value is used for future calls, until the next environment tick.

Utils

  • utils.percentile(arr, n) calculates the n-th percentile value of an array of numbers, with n a number between 0 (0-th percentile, or minimum value) and 1 (100th percentile, or maximum value). If a percentile falls between discrete values of the array, it linearly interpolates between those values.

0.3.13

21 Jan 11:20
80ef9c7
Compare
Choose a tag to compare

Updated to utils.sample to allow for weighted sampling of elements from an array. Now you can pass a second parameter to sample, an array of weights (numbers) that will allow certain values to be picked more or less often. Given an array of three elements, if you want the first to be chosen about 10% of the time, the second about 15%, and the third about 75%, you can do this:

const arr = ["1st value", "2nd value", "3rd value"];
const randomValue = utils.sample(arr, [10, 15, 75]);

Although in the above example the weights add up to 100, this is just to make the code easier to read and reason about. The weights can add up to anything and it will still calculate the percentage based off of the sum of the weights — for example, weights [1, 1, 3] map to 20%, 20%, and 60%, respectively.

0.3.12

13 Jan 19:09
5fc889c
Compare
Choose a tag to compare

Streamlining the API for Agents. Agents can now be instantiated with data, as opposed to instantiating and then setting data, like so:

// Before v0.3.12:
const agent = new Agent();
agent.set({
  x: 1,
  y: 2,
  z: 3
});
// Now:
const agent = new Agent({
  x: 1,
  y: 2,
  z: 3
});

Also, Agent rule functions can now have a return value, which gets set asynchronously (previously this had to be done using agent.enqueue().

// Before v0.3.12:
function tick(agent) {
  const averageSize = utils.mean(environment.getAgents().map(a => a.get('size'));
  // needs to be set asynchronously, otherwise subsequent agents in
  // the loop will get different values for `averageSize`
  agent.enqueue(() => agent.set('size', averageSize));
}
// Now:
function tick(agent) {
  const averageSize = utils.mean(environment.getAgents().map(a => a.get('size'));
  // is automatically set asynchronously
  return {
    size: averageSize
  });
}

0.3.11

09 Jan 20:04
74f65c7
Compare
Choose a tag to compare

Improved LineChartRenderer visualization — charts now update scale, size, etc. to fit data over time, rather than being fixed at time of instantiation.

New configuration options:

  • autoScale (defaults to false): If set to true, the LineChartRenderer will attempt to fit any data that goes out-of-bounds (vertically above or below the given range, or horizontally beyond the width of the chart) and will dynamically resize the existing visualization to fit all lines in the bounds of the chart.
  • autoScroll (defaults to false): if set to true, any data that would go out-of-bound horizontally (as time passes) will push the earliest data off to the left, resulting in a horizontally scrolling view of the data.
  • If both autoScale and autoScroll are set to true, then the chart will only scale vertically (for data above or below the given range) and will respect the horizontally scrolling view of the data

New configuration default:

  • The range object now defaults to { max: 1, min: 0 }, rather than { max: 500, min: 0 }

This release also introduces an abstract type, NumArray, to store numeric data in a more performant way than native JavaScript arrays. For now this will probably just remain an internal helper.

0.3.7

05 Jan 11:12
7b98530
Compare
Choose a tag to compare

New Histogram configuration options:

  • buckets parameter can now take either a single number representing the number of buckets to draw, or an array (containing anything) that will draw discrete values matching agent values, for example [true, false] or ["red", "blue"]. If buckets is an array, the min, max, belowMin, and aboveMax options are ignored.
  • A new epsilon parameter. If buckets is an array of numbers and epsilon is used, it will match any values within epsilon of the bucket values. For example, if buckets is [1, 2, 3], with epsilon 0.01, an agent with a value 1.01 would fall into bucket 1 but an agent with a value 1.5 would not be bucketed.
const histogram = new Histogram({
  buckets: [1, 2, 3],
  epsilon: 0.01
});