Carrot Search FoamTree

API reference, version 3.5.0

Carrot Search FoamTree is a modern, in-browser implementation of hierarchical Voronoi Treemaps. With animated transitions and zooming, varied layouts and visual customizations, FoamTree can provide an engaging user experience.

Technically, FoamTree is an HTML5 application, all modern desktop and mobile browsers supporting the canvas specification will be able to display the visualization. FoamTree can be integrated into an HTML page using a simple JavaScript API.

An up-to-date version for the latest stable release of FoamTree is always at: get.carrotsearch.com/foamtree/api.

You can get your first FoamTree visualization in 3 simple steps:

  1. Include carrotsearch.foamtree.js in your page.
  2. Define an HTML element in which FoamTree should be embedded. The element must have non-zero dimensions.
  3. Initialize FoamTree by creating a new instance of the CarrotSearchFoamTree class. In the options object passed to the constructor, provide the the identifier of your HTML element and some data to visualize.

Below is a complete working example illustrating the above steps. Please note you may need to correct the path to the carrotsearch.foamtree.js script to match the directory structure of your page.

<!DOCTYPE html>
<html>
  <head>
    <title>FoamTree Quick Start</title>
    <meta charset="utf-8" />
  </head>

  <body>
    <div id="visualization" style="width: 800px; height: 600px"></div>

    <script src="../carrotsearch.foamtree.js"></script>
    <script>
      window.addEventListener("load", function() {
        var foamtree = new CarrotSearchFoamTree({
          id: "visualization",
          dataObject: {
            groups: [
              { label: "Your", weight: 1.0 },
              { label: "First", weight: 3.0 },
              { label: "FoamTree", weight: 2.0 },
              { label: "Visualization", weight: 4.0 }
            ]
          }
        });
      });
    </script>
  </body>
</html>

Requirements

FoamTree requires a HTML5-compliant browser to run. In particular, it uses the canvas and local storage parts of the specification, as well as ECMAScript 5 JavaScript constructs. Most modern browsers meet the requirements and can run FoamTree.

FoamTree is officially supported on the following browsers:

FoamTree will most likely work on other modern browsers (such as Opera), though support for these is not a priority.

Distribution package

FoamTree distribution package contains the following files and directories:

  • api/: FoamTree API reference.
  • demos/: a number of complete demo applications illustrating specific aspects of FoamTree.
  • tests/: FoamTree's test suite.
  • carrotsearch.foamtree.js: FoamTree implementation, required at runtime.
  • carrotsearch.foamtree.asserts.js: validates option values passed to FoamTree, useful for development and debugging, not required at runtime.
  • carrotsearch.foamtree.util.*.js: utility scripts that handle a number of typical programming tasks around FoamTree, such as showing and hiding a Loading... indicator when new data is loading.

NPM dependency

Since 3.5.0 FoamTree is available as an NPM dependency for use in modern application build systems. The installation is different for the branded demo and the licensed non-branded variants, see the following sections for details.

Demo version

To add the FoamTree demo version to your project, run:

npm install @carrotsearch/foamtree

or

yarn add @carrotsearch/foamtree

Once the FoamTree dependency is installed, you can import the FoamTree class as follows:

  import { FoamTree } from "@carrotsearch/foamtree";

  const foamtree = new FoamTree({ ... });

If you hold a FoamTree license, see the licensed version section for installation instructions.

Licensed version

The NPM version of licensed FoamTree is available from our servers through a special URL. To add the licensed FoamTree dependency to your project:

  1. Upload your FoamTree license file at https://secure.carrotsearch.com.

  2. Copy the URL of the "npm" link under the FoamTree version you'd like to install.

    Carrot Search software downloads site, link to the dependency in the npm format
  3. Paste the copied link to your package.json as follows:

    {
      "dependencies": {
        "@carrotsearch/foamtree": "https://secure.carrotsearch.com/download/foamtree/3.5.0/npm?A1f8b080000000000000a6d504b4ec33010dd738aa12b90eac476d33845265d80b8005c60624f68a4c6ae6cb7d0db13d2a42a12b37b9f79cf1ebdef0cb94870a2103bef9e1722938bfa0e007467e39142fd8221f804ef84c1ec206626d3f9248db67bc6e015134568fd7eefbf9ec05ad6f7ec3c0c3c583c2fa1f72eed96701e221e0757e83181f1ee442e0d9d193076d3686151312e98e4524c6df622d3f7a10b146b47c36b753ec351ebb173891c3a436c16849a72a4ceffd3c7c5e900f4f79f3abff2a3eb10bc3d9a74d9b92166fc4b39eca97ef3d87f04229d8f70b6e7b7fe2b1ae2ae45b5dec6eed3613a068292e486aaaa5c159c3756e2da72a9d62d29894565572b6a0ba38a6693735e59594ace2dca4271c13909d5aa0605aab62d951054c94dd954b8ad7f00604b5bd8ec010000Z"
      }
    }

    The hexadecimal data included in the query string of the URL represents your FoamTree license file and allows our servers to determine which FoamTree versions your license file covers.

    Alternatively, you can download the *.tgz file from the copied link, store it together with your code and reference the downloaded *.tgz file in your package.json.

  4. Once the FoamTree dependency is installed, import the FoamTree class and plugin classes as follows:

    import { FoamTree } from "@carrotsearch/foamtree";
    
    const foamtree = new FoamTree({ ... });

API Overview

The core implementation and API of the visualization is contained in the carrotsearch.foamtree.js file. It handles all your code's interactions with the visualization, including embedding, changing visual properties, loading new data and listening to events.

The entry point to the API is an instance of the CarrotSearchFoamTree class created during embedding.

Important concepts

The remaining part of this documentation refers to a number of concepts defined below.

data model
the data FoamTree can visualize; consists of a hierarchical structure of groups
group
an individual piece of data FoamTree can visualize; represented by a polygon
rollout
the animated effect FoamTree uses to show a new data model
pullback
the animated effect FoamTree uses to hide an existing data model

Group states

Each group shown in the visualization can be in a combination of four different states (see the image below):

selected / unselected
groups selected by the user are drawn with a thick outline
open / closed
if a group is open, its label and polygon are not painted so that the user can interact with its child groups
exposed / unexposed
an exposed group gets zoomed in, so that the user can inspect its contents more easily; groups that are not exposed at the moment are zoomed out and dimmed
hovered
a group over which the user's pointer is hovering gets highlighted

Group decorations

Each group is drawn with a number of customizable graphical decorations, shown in the image below:

border width
the distance between neighboring groups of the same level
inset width
the distance between a group's border and its immediate child groups' borders
stroke width
width of the line drawn along the group's border
selection outline width
width of the line drawn to mark selected groups
border radius
radius of the group's border

Interaction

Users can interact with FoamTree in the following ways:

On desktop device On touch device Action
left click tap select group, again to deselect
Ctrl + left click
+ left click
select multiple groups
left double click double tap expose group
right double click
Shift + left double click
two-finger double tap unexpose group
left click-and-hold tap-and-hold open group
right click-and-hold
Shift + left click-and-hold
two-finger tap-and-hold close group
mouse wheel pinch zoom in / out
mouse drag drag pan around zoomed visualization
Esc
rapid zoom out
three-finger pinch unexpose and close all groups, reset zoom factor

You can use the interaction hints utility to display a simple interaction guide for the users. If needed, you can change the default interaction mappings.

Embedding

Before FoamTree can display anything, it needs to be embedded in your HTML page. To embed FoamTree, your page needs to:

  1. Load carrotsearch.foamtree.js, which contains FoamTree implementation. You will usually use a dedicated script tag for this, but you can also concatenate FoamTree code with some other JavaScript on your page to speed up page loading.
  2. Define the HTML element that will contain FoamTree visualization. FoamTree will occupy the full width and height of the element.
  3. Inspect the static supported property of the visualization class to make sure the browser supports FoamTree.
  4. Initialize FoamTree by calling the CarrotSearchFoamTree constructor and providing the options object as a parameter. The only required option during initialization is the id or the element option that need to point to the HTML element you defined earlier. In most cases, you will be providing other options during initialization as well, such as the dataObject to display or some visual customizations. See the options reference for a complete list of options.

Heads up!

The element HTML needs to have non-zero dimensions at the time FoamTree initializes. In most cases it is enough to initialize FoamTree once the DOM is ready. In certain cases, however, especially when the page is accessed through the file:// protocol, the element will receive its size a bit later. To avoid race conditions, you can initialize FoamTree in the onload event.

<script>
window.addEventListener("load", function() {
  // Perform FoamTree embedding here
});
</script>
Touch devices

Support for touch-based interaction requires some extra embedding steps. Please see the touch devices section for the details.

The following example shows typical embedding code with a minimal data model. This will also be our baseline for examples where the embedding isn't explicit.

if (CarrotSearchFoamTree.supported) {
  var foamtree = new CarrotSearchFoamTree({
    id: "visualization",
    dataObject: {
      groups: [
        { id: "1", label: "Group 1", groups: [
          { id: "1.1", label: "Group 1.1" },
          { id: "1.2", label: "Group 1.2" }
        ]},
        { id: "2", label: "Group 2", groups: [
          { id: "2.1", label: "Group 2.1" },
          { id: "2.2", label: "Group 2.2" }
        ]},
        { id: "3", label: "Group 3", groups: [
          { id: "3.1", label: "Group 3.1" },
          { id: "3.2", label: "Group 3.2" }
        ]},
        { id: "4", label: "Group 4", groups: [
          { id: "4.1", label: "Group 4.1" },
          { id: "4.2", label: "Group 4.2" }
        ]},
        { id: "5", label: "Group 5", groups: [
          { id: "5.1", label: "Group 5.1" },
          { id: "5.2", label: "Group 5.2" }
        ]}
      ]
    }
  });
} else {
  console.log("Visualization not supported.");
}

The reference to the returned instance (the foamtree variable) can be used to change options of the visualization or load new data model. The instance is ready to handle method calls as soon as the constructor invocation returns.

Changing options

After FoamTree initializes you can still change the values of most options using the set method. Most commonly, you will be setting a new value for the dataObject option to visualize new data.

The following example changes the groupSelectionOutlineColor to paint the outline in red. Run the example and select some groups to see the result.

foamtree.set("groupSelectionOutlineColor", "red");
Heads up!

When changing certain visual properties using the set method, for the changes to take effect, you will need to explicitly instruct FoamTree to redraw the visualization.

The following code increases the group border and stroke widths. We deliberately defer the call to call the redraw method to show that setting the options alone does not update the visualization image and an explicit redraw is needed.

var timeout;
timeout = window.setTimeout(function() {
  foamtree.set({
    groupBorderWidth: 20,
    groupInsetWidth: 40,
    groupStrokeWidth: 10
  });

  // Redraw after a while
  timeout = window.setTimeout(foamtree.redraw, 1000);
}, 500);

Data model

A data model can be loaded, replacing the current model, by passing the dataObject parameter either during embedding or by calling the set method, as in the example below.

foamtree.set({
  dataObject: {
    groups: [
      { label:"Group 1", groups: [
        { label:"Group 1.1" },
        { label:"Group 1.2" },
        { label:"Group 1.3" }
      ]},
      { label:"Group 2", groups: [
        { label:"Group 2.1" },
        { label:"Group 2.2" }
      ]},
      { label:"Group 3" }
    ]
  }
});

If the data model is not in the format presented above, it needs to be converted prior to visualization. In this example we will load JSON data, convert it to FoamTree format and visualize. We use jQuery to make Ajax requests, but any other library should work equally well. Note how the model changes after a short pause (fetching model data dynamically, network connection is required).

foamtree.set({
  dataObject: { groups: [ { label: "loading..." }, { label: "Please wait" } ] },
  fadeDuration: 500
});

$.ajax({
  url: "../demos/assets/data/census.json",
  dataType: "json",
  success: function(data) {
    var states = data.groups;

    var races = [
      { label: "Hispanic", prop: "PctHisp" },
      { label: "White",    prop: "PctNonHispWhite" },
      { label: "Black",    prop: "PctBlack" },
      { label: "Indian",   prop: "PctAmInd" },
      { label: "Asian",    prop: "PctAsian" },
      { label: "Hawaiian", prop: "PctNatHawOth" }
    ];

    var popByRace = states.reduce(function (index, state) {
      races.forEach(function (spec) {
        var pop = state[spec.prop] * state.Pop;
        index[spec.prop].total += pop;
        index[spec.prop].min = Math.min(index[spec.prop].min, state[spec.prop]);
        index[spec.prop].max = Math.max(index[spec.prop].max, state[spec.prop]);
      });
      return index;
    }, races.reduce(function (index, spec) {
         index[spec.prop] = { total: 0, min: Number.MAX_VALUE, max: 0 };
         return index;
       }, { }));

    var groups = races.map(function(spec) {
      return {
        label: spec.label,
        weight: popByRace[spec.prop].total,
        groups: states.map(function(state) {
          return {
            label: state.Placename,
            weight: (state[spec.prop] - popByRace[spec.prop].min) /
                      popByRace[spec.prop].max
          };
        })
      };
    });

    foamtree.set({
      dataObject: { groups: groups },
      rolloutDuration: 3000
    });
  }
});

To clear the visualization, set a null dataObject. If you set any other options along with the new data object, the new options will apply both during the pullback of the current model and rollout of the new model. The example below clears the current model but also sets the duration of the pullback animation. As a result, the current model will disappear in an animated fashion.

var timeout = window.setTimeout(function() {
  foamtree.set({
    pullbackDuration: 1000,
    dataObject: null
  });
}, 1000);

Selection, opening and exposure

Each group shown by FoamTree can get selected, opened or exposed either as a result of user's interactions or through an API call. You can get and set selection, open and exposure state in a number of ways.

Getting selection, opening and exposure

You can get information about the currently selected, open and exposed groups by getting the values of the select, open and exposure options, respectively. To get notified about the state changes right after they are made by the user, register listeners for the corresponding events.

Setting selection, opening and exposure

You can change selection, opening and exposure in three different ways:

  • By setting the select, open and exposure options.
  • By calling the select, open and expose methods. The difference compared to setting options is that the methods return a promise that gets resolved when the animations related to the state change have completed.

  • By providing the initial group states in the data object using the selected, open and exposed as in the example below.

    foamtree.set("groupSelectionOutlineColor", "#f00");
    foamtree.set("dataObject", { groups: [
      {
        label: "Initially exposed, open & selected",
        exposed: true, open: true, selected: true, groups: [
          { label: "Initially selected", selected: true },
          { label: "Unselected" }
        ]
      },
      {
        label: "Initially open", open: true, groups: [
          { label: "Initially selected", selected: true },
          { label: "Unselected" }
        ]
      }
    ]});
Heads up!

When group selection, opening or exposure is changed through an API call, the relevant event listeners will not be called. Please also see the limitations when relaxation is visible.

Events

Event listeners are functions invoked by the visualization code when certain conditions or triggers are met. Some of these callbacks are invoked only for user-related actions (for example onGroupSelectionChanged), others may be a result of internal state changes (like onRolloutComplete). Please see the event reference for a list of all available events.

You can manage event listeners in two ways:

  • By getting or setting the related options, or, most commonly, by providing the options during initial embedding. For each option, you can provide an individual function or an array of functions. However, when you get the option value, always an array of functions will be returned, even if there is only one function registered for a specific event. Setting a new function for a specific event will remove all previously registered functions for that event.

    The example below initializes the visualization and registers for notifications about the changes of group selection and exposure.

    var foamtree = new CarrotSearchFoamTree({
      id: "visualization",
      dataObject: {
        groups: [
          { label: "Try" }, { label: "Selecting" },
          { label: "Or" }, { label: "Exposing" },
          { label: "Some" }, { label: "Group" },
          { label: "And" }, { label: "Watch" },
          { label: "The" }, { label: "Console" }
        ]
      },
      onGroupSelectionChanged: function (info) {
        window.console.log("selected", info);
      },
      onGroupExposureChanged: [
        function(info) {
          window.console.log("exposed 1", info);
        },
        function(info) {
          window.console.log("exposed 2", info);
        }
      ]
    });
  • By calling the on and off methods, to add and remove event listeners, respectively. As opposed to using the set method, the on and off methods preserve the previously registered listeners.

    Both the on and off method takes two parameters: the type of the event and the event listener to add or remove. The example code below adds group selection and exposure listeners:

    var foamtree = new CarrotSearchFoamTree({
      id: "visualization",
      dataObject: {
        groups: [
          { label: "Try" }, { label: "Selecting" },
          { label: "Or" }, { label: "Exposing" },
          { label: "Some" }, { label: "Group" },
          { label: "And" }, { label: "Watch" },
          { label: "The" }, { label: "Console" },
        ]
      }
    });
    foamtree.on("groupSelectionChanged", function (info) {
      window.console.log("selected", info);
    });
    foamtree.on("groupExposureChanged", function(info) {
      window.console.log("exposed 1", info);
    });
    foamtree.on("groupExposureChanged", function(info) {
      window.console.log("exposed 2", info);
    });
    Heads up!

    To get the type string to use with the on / off method, take the event option name, remove the leading "on" and lower-case the first letter, for example:

    Option name for the set method Event name for the on / off methods
    onGroupSelectionChanged groupSelectionChanged
    onGroupExposureChanged groupExposureChanged
    onRolloutComplete rolloutComplete

For advanced topics related to event listeners, see changing event mapping and externally triggered interaction.

Resizing

If the visualization container element changes its size, the visualization will be scaled proportionally. To redraw it from scratch taking the new size into consideration, you will need to explicitly call the resize method. Because resizing can be costly, the most common pattern is to listen to the window's resize event and call FoamTree's resize method a while after resizing stops:

window.addEventListener("resize", (function() {
  var timeout;
  return function() {
    window.clearTimeout(timeout);
    timeout = window.setTimeout(foamtree.resize, 300);
  };
})());

To compare the behavior of visualization with and without the resize listener, resize the browser window once the example is visible.

// With the resize listener installed.
foamtree.on("relaxationStep", function() {
  console.log("Layout update");
});
// Without the resize listener.
foamtree.on("relaxationStep", function() {
  console.log("Layout update");
});

On mobile devices one should resize and redraw the visualization after the orientation of a mobile device changes:

window.addEventListener("orientationchange", function() {
  foamtree.resize();
});

Image data

You can get the current image of the visualization as a Data URI by getting the imageData option. You can then display the image for the user, as show in the example below, or let the user download the image.

foamtree.set({
  dataObject: { groups: [
    { label: "Click me to get the image", trigger: true, weight: 5 },
    { label: "Group 1" },
    { label: "Group 2" },
    { label: "Group 3" },
    { label: "Group 3" }
  ]},
  onGroupClick: function (event) {
    if (event.group.trigger) {
      event.preventDefault();
      window.open(foamtree.get("imageData", {
        format: "image/png"
      }));
    }
  }
});

Printing

FoamTree is a regular printable element of the page. You may want to customize the printing style sheets to hide non-relevant elements. To get a higher-resolution bitmap for printing, you can increase the resolution of the imageData by providing the pixelRatio parameter:

window.open(foamtree.get("imageData", {
  format: "image/png",
  pixelRatio: 1.5
}));

Debugging

To minimize the size of the code required at runtime, FoamTree does not verify whether input options are valid. During development, however, it is strongly recommended to turn on assertions by including the carrotsearch.foamtree.asserts.js file. This will automatically enable assertion checking for options passed to the visualization via the set method.

The example below shows invalid values of certain options and how they result in console warnings.

foamtree.set({
  id: undefined,
  onGroupClick: "fooBar",
  imageData: "newImage.png",
  showZeroWeightGroups: "true",
  groupFillType: "invalid-value",
  unknownOption: 1
});

Carrot2 data models

If you are using Carrot2 or Lingo3G clustering engine, you can convert the JSON and XML data formats using the following jQuery snippet.

// Convert clusters to groups from Carrot2/Lingo3G JSON format.
function convert(clusters) {
  return clusters.map(function(cluster) {
    return {
      id:     cluster.id,
      label:  cluster.phrases.join(", "),
      weight: cluster.attributes && cluster.attributes["other-topics"] ? 0 : cluster.size,
      groups: cluster.clusters ? convert(cluster.clusters) : []
    }
  });
};

// Clear the previous model.
foamtree.set("dataObject", null);

// Load Carrot2 JSON clusters.
$.ajax({
  url: "../demos/assets/data/carrot2/ben-and-jerry.json",
  dataType: "json",
  success: function(data) {
    foamtree.set({
      dataObject: {
        groups: convert(data.clusters)
      }
    });
  }
});

And a similar conversion from the XML format.

// Convert clusters to groups from Carrot2/Lingo3G XML format.
function convert(clusters) {
  return $(clusters).map(function(index, cluster) {
    cluster = $(cluster).detach(); // destroys the model, simplifies processing.
    var subgroups = cluster.children("group").size() > 0 ? convert(cluster.children("group")) : [];
    return {
      id:     cluster.attr("id"),
      label:  cluster.find("group > title > phrase").map(asText).toArray().join(", "),
      weight: cluster.attr("score") ? cluster.attr("score") : 0,
      groups: subgroups
    }
  }).toArray();
};

// Convert a node to text.
function asText(i, e) { return $(e).text(); }

// Clear the previous model.
foamtree.set("dataObject", null);

// Load Carrot2 JSON clusters.

$.ajax({
  url: "../demos/assets/data/carrot2/ben-and-jerry.xml",
  dataType: "xml",
  success: function(data) {
    foamtree.set({
      dataObject: {
        groups: convert($(data).find("searchresult > group"))
      }
    });
  }
});

Touch devices

FoamTree core does not contain support for detecting touch-based events, but delegates this to a specialized library. This kind of decoupling allows improvements and bug fixes related to touch event detection to happen independently of FoamTree releases.

There are two ways to enable touch device support in FoamTree:

  • Include the Hammer.js library. If FoamTree detects that Hammer.js is available, it will use it to detect touch events and trigger the appropriate actions. Use Hammer.js version 1.0.6, 1.0.7, 1.0.8 or 1.0.9. FoamTree currently does not support Hammer.js 1.1.x and 2.x lines, but you can use them in the externally triggered interaction mode.
  • Externally triggered interaction. If you'd like to use a different touch event detection library or your own code, your code can directly trigger the appropriate events.

Advanced usage

Default colors

The default way of assigning colors to groups and labels in a model is the "rainbow" color model. The model assigns colors to the first-level groups from the range of hue values defined by the rainbowStartColor and rainbowEndColor options. Depending on the value of the rainbowColorDistribution options, the colors will be distributed radially or linearly across the whole visualization area.

The example below shows a set of groups covering the full spectrum of hues (angles between 0 and 360).

foamtree.set({
  rainbowStartColor: "hsla(  0, 100%, 50%, 1)",
  rainbowEndColor:   "hsla(360, 100%, 50%, 1)",
  dataObject: {
    groups: (function() {
      var arr = [];
      for (var i = 0; i < 100; i++) {
        arr.push({label: ""});
      }
      return arr;
    })()
  }
});

Child groups are recursively assigned with colors that introduce a gradient to the lightness components of their parent's color, as demonstrated in the example below, which shows HSL color values assigned to each group.

function modelGen(subgroups) {
  var arr = [];
  var i;
  for (i = subgroups.pop(); i > 0; i--) {
    arr.push({label: ""});
  }
  if (subgroups.length > 0) {
    for (i = arr.length; --i >= 0;) {
      arr[i].groups = modelGen(subgroups.slice());
    }
  }
  return arr;
}

foamtree.set({
  rainbowStartColor: "hsla(  0, 100%, 50%, 1)",
  rainbowEndColor:   "hsla(360, 100%, 50%, 1)",
  groupColorDecorator: function(opts, params, vars) {
    params.group.label = "" +
              Math.round(vars.groupColor.h) + ", " +
              Math.round(vars.groupColor.s) + "%, " +
              Math.round(vars.groupColor.l) + "%";
  },
    dataObject: modelGen([3, 5, 10, 1])[0]
});

If the default color model does not suit your needs you can customize group colors to your liking by using the groupColorDecorator callback.

Custom colors

Varying the polygon's color based on some property of the corresponding group can help to convey some more information about the data set. For the sake of example, let us revisit the visualization of USA Today census data introduced in the data model section. The original code used the size (weight) of the groups to show how large is the specific ethnic group in the corresponding state. The example below additionally visualizes the USA Today diversity index using the lightness of the polygon – the lower the diversity index, the darker the color.

foamtree.set({
  dataObject: { groups: [ { label: "loading..." }, { label: "Please wait" } ] },
  fadeDuration: 500
});

$.ajax({
  url: "../demos/assets/data/census.json",
  dataType: "json",
  jsonpCallback: "callback",
  success: function(data) {
    var states = data.groups;

    var races = [
      { label: "Hispanic", prop: "PctHisp" },
      { label: "White",    prop: "PctNonHispWhite" },
      { label: "Black",    prop: "PctBlack" },
      { label: "Indian",   prop: "PctAmInd" },
      { label: "Asian",    prop: "PctAsian" },
      { label: "Hawaiian", prop: "PctNatHawOth" }
    ];

    var popByRace = states.reduce(function (index, state) {
      races.forEach(function (spec) {
        var pop = state[spec.prop] * state.Pop;
        index[spec.prop].total += pop;
        index[spec.prop].min = Math.min(index[spec.prop].min, state[spec.prop]);
        index[spec.prop].max = Math.max(index[spec.prop].max, state[spec.prop]);
      });
      return index;
    }, races.reduce(function (index, spec) {
         index[spec.prop] = { total: 0, min: Number.MAX_VALUE, max: 0 };
         return index;
       }, { }));

    var groups = races.map(function(spec) {
      return {
        label: spec.label,
        weight: popByRace[spec.prop].total,
        groups: states.map(function(state) {
          return {
            label: state.Placename,
            weight: (state[spec.prop] - popByRace[spec.prop].min) /
                      popByRace[spec.prop].max,

            // Pass the diversity index as an extra property
            diversity: state.USATDiversityIndex
          };
        })
      };
    });

    foamtree.set({
      dataObject: { groups: groups },
      rolloutDuration: 3000,
      groupColorDecorator: function (opts, props, vars) {
        var diversity = props.group.diversity;
        if (diversity) {
          // If diversity is available, use it to
          // vary the lightness of the group.
          // We assume the diversity is in the 0..1 range.
          vars.groupColor.l = diversity * 100;
        }
      }
    });
  }
});

Please see the documentation of the groupColorDecorator option for more color customization examples.

Custom content

If needed, you can draw custom content in each FoamTree polygon. Please see the groupContentDecorator option for details and examples. Additionally, the images demo shows how to render SVG images inside FoamTree polygons and the GitHub search demo demonstrates some more advanced custom content layout and rendering.

Choosing layout type

As of version 3.3.0, FoamTree can produce both the organic-looking polygonal layouts and the traditional rectangular layouts. While the polygon-based layout is the default, the rectangular layouts may sometimes be useful. The documentation for the layout option summarizes the available arrangements and highlights the possible use cases.

As of version 3.4.0, you can additionally choose between the hierarchical and flattened presentation mode. Please see the stacking option for details and use cases.

Visualizing large data sets

Since version 3.2.0, FoamTree should be able to visualize hierarchies of 100k+ groups with 100+ levels on recent hardware. As of version 3.3.0, FoamTree can also generate traditional rectangular treemaps which are significantly faster to compute than the default polygon-based layout. The following subsections discuss the performance and visual aspects involved.

Visualization layout computation performance

Before FoamTree can draw anything, it needs to compute the layout based on the input data. An important component of the layout algorithm is determining the positions and areas of polygons in such a way that the areas correspond to group's weights as closely as possible. To achieve a decent representation accuracy, FoamTree needs to iteratively update positions of all polygons until the accuracy reaches the required level. We call this process relaxation.

For the curious

By default, FoamTree performs relaxation off-screen and does not show the intermediate positions of the polygons. To observe what relaxation looks like, you can set the relaxationVisible option to true:

foamtree.set({
  fadeDuration: 1500,
  dataObject: {
    groups: (function() {
      var arr = [];
      for (var i = 0; i < 80; i++) {
        arr.push({
          label: "",
          weight: Math.pow(Math.random(), 5) +
                  (Math.random() < 0.1 ? Math.random() * 2 : 0)
        });
      }
      return arr;
    })()
  },

  // Show the relaxation
  relaxationVisible: true,

  // Make the relaxation last longer
  relaxationQualityThreshold: 0,
  relaxationMaxDuration: 15000,

  // For faster rendering
  groupFillType: "plain"
});

To make it possible to browse very large hierarchies, since version 3.2.0 FoamTree defers laying out a group's children until the child groups need to be drawn. With the default value of the maxGroupLevelsDrawn option, FoamTree will eagerly compute the layout for the first 4 levels of the hierarchy. Further layouts will be computed on demand as the user opens the top-level groups.

The on-demand computation of layout works very well when the large number of input groups is spread over a balanced tree of a sufficient number of level. If, however, there are individual groups with large numbers of child groups, the layout computation can still take some significant time. You can try some of the following tuning tips to improve the performance in such cases:

  • Since 3.2.0 Decrease maxGroupLevelsDrawn. With a lower number of layouts computed up-front, the initial rendering as well as group opening time should be much lower.
  • Since 3.4.5 Use deferred layout computation. The maxGroupLevelsAttached option along with the attach method can be used to defer the layout of lower hierarchy levels until the layout of the top level is computed and shown to the user. This can improve the responsiveness of the visualization during the initial data loading. See the Deferred layout demo for a complete code example.
  • Disable rollout and pullback animations. As the pullback animations move each group in separation, animating thousands will significantly degrade the performance. If your data contains groups with more than several hundreds direct child groups, consider setting rolloutDuration and pullbackDuration to 0, which will apply simple fade-in and fade-out effects.
  • Replace expose effect with a simple zoom. Similarly to rollout and pullback animations, the expose effect needs to animate each group individually. If your data contains more than several hundreds of groups, use the following code to change the default double-click action from expose to zoom in:
    foamtree.set({
      dataObject: largeDataSet,
    
      // Store references to parent groups
      onModelChanging: function addParent(group, parent) {
        group.parent = parent;
        if (group.groups) {
          group.groups.forEach(function(g) {
            addParent(g, group);
          });
        }
      },
    
      // Expose is expensive for large hierarchies.
      // Therefore, when the user double-clicks a group, zoom in to the group.
      onGroupDoubleClick: function (e) {
        e.preventDefault();
        var group = e.secondary ? e.bottommostOpenGroup : e.topmostClosedGroup;
        var toZoom;
        if (group) {
          this.open({ groups: group, open: !e.secondary });
          toZoom = e.secondary ? group.parent : group;
        } else {
          toZoom = this.get("dataObject");
        }
        this.zoom(toZoom);
      }
    });
  • Since 3.2.0 Use rectangular layout by setting layout to ordered or squarified. The rectangular layouts do not need preform relaxation and are therefore much faster to compute.
  • Increase relaxationQualityThreshold. If you'd like to keep the polygonal layout with large data sets, lowering layout quality may help performance. Try a number of values for relaxationQualityThreshold, starting from 0.5 through 1.0 up to 10 or so. This will cause FoamTree to perform fewer relaxation iterations and thus improve the layout calculation time. The down side of this kind of optimization is less appealing layout, with a large number of elongated polygons. In extreme cases, you can also try setting relaxationQualityThreshold to Number.MAX_VALUE. In this case, relaxation will be completely disabled. This may be the only way to proceed with very large data sets.
  • Set relaxationInitializer to ordered or squarified. The treemap-initialized layouts are slightly faster to relax and also lead to more pleasing shapes when the number of relaxation iterations is low or when there is no relaxation at all.
  • Show relaxation to the user. In addition to the above tuning, you may want to show the relaxation process to the users, so that they don't see the blank screen while waiting for the visualization to settle. You can do that by setting relaxationVisible to true. Additionally, you can cap the total relaxation time using the relaxationMaxDuration option. The total duration of the relaxation in this mode (assuming there is no cap on duration) will be larger compared to the invisible relaxation mode due to a slightly different relaxation algorithm and time spent on drawing intermediate layouts. However, you will be able to deliver some image to the users more quickly.

The following examples show a 1000-group data set visualized with polygonal and rectangular layouts.

Polygonal layout with the squarified treemap initializer and a minimum amount of relaxation.

var time;
foamtree.set({
  fadeDuration: 1500,
  dataObject: {
    groups: randomGroups(1000)
  },

  relaxationInitializer: "squarified",
  relaxationQualityThreshold: 6,

  groupBorderWidth: 2,

  // Measure and report layout time
  onModelChanging: function() {
    time = Date.now();
  },
  onRolloutStart: function() {
    console.log("Polygonal layout took " +
      (Date.now() - time) + " ms.");
  }
});

Rectangular layout using the squarified treemap algorithm, relaxation is not performed in this case.

var time;
foamtree.set({
  fadeDuration: 1500,
  dataObject: {
    groups: randomGroups(1000)
  },

  layout: "squarified",
  // relaxation qualiti does not apply here

  groupBorderWidth: 2,

  // Measure and report layout time
  onModelChanging: function() {
    time = Date.now();
  },
  onRolloutStart: function() {
    console.log("Rectangular layout took " +
      (Date.now() - time) + " ms.");
  }
});

Visualization rendering performance

Another important performance factor is the actual drawing of the visualization image. To achieve highest rendering speeds, FoamTree employs a number of optimizations:

  • Since 3.2.0 Drawing only the specified number of group levels. With a very deeply nested hierarchy and the default value of the parentFillOpacity option only a small number of top layers would be visible. Taking advantage of this, FoamTree does not draw the lower levels of groups until some of their parents gets open for browsing. You can use the maxGroupLevelsDrawn and maxGroupLabelLevelsDrawn options to customize how many levels of group polygons and labels should be drawn.

  • Incremental updates. When only part of the visualization image needs to be updated, for example on group hover or selection change, FoamTree tries to perform an incremental redraw in which only the affected area is cleared and drawn again. This significantly speeds up redrawing the visualization, especially for large data sets.

    Heads up!

    In certain cases, incremental updates are not possible:

    • when some groups are currently exposed
    • when groupBorderWidth is 0
    • when groupBorderWidth is less than groupStrokeWidth/ 2 + 0.5
    • when any group is selected and groupBorderWidth is less than groupSelectionOutlineWidth/ 2 + 0.5 +groupSelectionOutlineShadowSize

    To find out whether the redraw operation was incremental or not, check the first parameter passed to the onRedraw event listener, if the parameter is true, the last rendering operation was an incremental one.

    The following example sets up the onRedraw listener to report on the type of redraws FoamTree is performing. Try interacting with the visualization and watch the console to see when incremental redraws are possible.

    foamtree.set({
      groupBorderRadius: 0,
      dataObject: { groups: [
        {
          label: "Click to unexpose and reset options\
                  [will enable incremental draws]",
          action: "reset"
        },
        {
          label: "Click to expose\
                  [will disable incremental draws]",
          action: "expose"
        },
        {
          label: "Click to set groupBorderWidth to 0\
                  [will disable incremental draws]",
          action: "border-width"
        },
        {
          label: "Click to set groupStrokeWidth to 10\
                  [will disable incremental draws]",
          action: "stroke-width"
        }
      ]},
      onRedraw: function (incremental) {
        console.log((incremental ? "Incremental" : "Full") + " redraw");
      },
      onGroupDoubleClick: function(event) {
        event.preventDefault();
      },
      onGroupClick: function (event) {
        var action = event.group.action;
        if (action) {
          event.preventDefault();
          switch (action) {
          case "reset":
            foamtree.expose([]).then(function() {
              foamtree.set({
                groupBorderWidth: 4,
                groupStrokeWidth: 1.5
              });
              foamtree.redraw();
            });
            break;
    
          case "expose":
            foamtree.expose(event.group);
            break;
    
          case "border-width":
            foamtree.set("groupBorderWidth", 0);
            foamtree.redraw();
            break;
    
          case "stroke-width":
            foamtree.set("groupStrokeWidth", 10);
            foamtree.redraw();
            break;
          }
        }
      }
    });
  • Wireframe and final rendering quality. When animating the visualization, either during rollout or in response to user actions, FoamTree will switch to the wireframe rendering mode. Depending on the size of the data set, wireframe rendering may disable certain expensive to draw elements, such as group gradients, shadows, stokes or labels. As soon as the animation stops, FoamTree will switch back to the final rendering quality and show the visualization with all the details.

    You can influence the amount of details drawn in both modes by changing the wireframeDrawMaxDuration, finalCompleteDrawMaxDuration and finalIncrementalDrawMaxDuration options. On high-DPI displays, you can also set separate pixel densities for the final and wireframe images using the pixelRatio and wireframePixelRatio options, respectively.

  • Caching of geometry information. If the geometry of the layout has not changed between redraws, which is the case when zooming and panning, FoamTree will be able to e.g. avoid re-fitting of the label's text into the polygon.

While FoamTree tries to automatically adjust the level of detail to match the size of the data set and the device speed, you can apply some of tuning described below to further improve the animation smoothness and rendering time:

  • Decrease maxGroupLevelsDrawn and maxGroupLabelLevelsDrawn.
  • Decrease the values of the wireframeDrawMaxDuration, finalCompleteDrawMaxDuration and finalIncrementalDrawMaxDuration options, possibly to zero.
  • Set groupFillType to plain.
  • Set groupStrokeType to none.
  • Set wireframeLabelDrawing to never.

Visualizing very deep hierarchies

When visualizing hierarchies with 5 or more levels with default option values it may happen that lower-level groups or branches are not rendered at all because there is not enough space in the parent group's polygons. The following tips will help you to ensure that as many groups as possible are rendered:

  • Set stacking to hierarchical for best performance.
  • Decrease groupMinDiameter to 0, so that FoamTree keeps laying out child groups until the floating point computation precision limit is reached.
  • Decrease groupBorderWidth and groupInsetWidth to allow more space for the child groups.
  • Set groupBorderRadius to 0, for deeply-nested hierarchies rounded borders may look distorted.

Lazy loading

With FoamTree 3.2.0 and later it is possible to defer the loading of data for lower levels of the hierarchy until the user opens the corresponding parent group. The lazy loading code example demonstrates this approach.

Changing default event mappings

If you would like to change the default event to action mappings, you can do so by registering a listener for the appropriate low-level event, such as onGroupClick, that would prevent the default behavior and perform the desired one.

The following example restores the FoamTree 2.0.x default double click interaction, which was to open the group on double click or Shift + click and close the group on Ctrl + Shift + click.

foamtree.set("onGroupDoubleClick", function(event) {
  event.preventDefault();
  this.open(event.group);
});

foamtree.set("onGroupClick", function(event) {
  if (event.shiftKey) {
    event.preventDefault();
    this.open({
      groups: event.ctrlKey ? event.bottommostOpenGroup : event.group,
      open: !event.ctrlKey
    });
  }
});

The following example shows how to prevent the visualization from resetting the view when the Esc key is pressed.

foamtree.set("onKeyUp", function(event) {
  if (event.keyCode === 27) {
    event.preventDefault();
  }
});

Finally, the example below exposes the group the user is hovering on rather than simply highlighting it. The example changes the exposure parameters a little to make the interaction smoother.

foamtree.set({
  onGroupHover: (function() {
    var timeout;
    return function(event) {
      event.preventDefault();
      window.clearTimeout(timeout);
      timeout = window.setTimeout(function() {
        foamtree.expose(event.group);
      }, 500);
    };
  })(),
  exposeDuration: 500,
  groupExposureScale: 1.1,
  groupExposureZoomMargin: 0.4,

  fadeDuration: 1500,
  dataObject: {
    groups: (function() {
      var arr = [];
      for (var i = 0; i < 100; i++) {
        arr.push({
          label: "",
          weight: 0.1 + Math.random() +
                  (Math.random() < 0.2 ? Math.random() * 3 : 0)
        });
      }
      return arr;
    })()
  }
});

Externally triggered interaction

You can completely override FoamTree's internal event capture layer and substitute it with your own code if needed. One typical use case is when you need to support a non-standard mobile device for which FoamTree's internal event capture does not work very well.

The general pattern for externally triggered interaction is to set the interactionHandler option to external, set up your own event capture code (using some of the available libraries, such as Hammer.js or Event.js and trigger the appropriate FoamTree actions using the trigger method. Please see the external-interaction.html demo for a complete example.

Utilities

FoamTree comes with a number of utility scripts that add some extra functionality to the core library. The utilities come as Apache License 2.0 source code, feel free to modify the code to suit your needs. If you don't need to use the utilities, you can still take a look at the source code for some examples of advanced usage of FoamTree API.

Loading indicator

Loading of data for the visualization but also computation of the diagram can take a while for larger data sets. You can use the carrotsearch.foamtree.util.loading.js utility to show a simple Loading... message to the user. Please see the source code of the loading.html example page for usage information.

Interaction hints and guide

To help the users to discover all the ways in which they can interact with FoamTree, you can use the carrotsearch.foamtree.util.hints.js utility. It will show contextual suggestions on how to browse the data (only on desktop browser) and also a guide to all possible interactions and keyboard bindings. Please see the source code of the hints.html example page for usage information.

Relaxation progress indicator

If FoamTree is set to show layout relaxation using the relaxationVisible option, you can use the carrotsearch.foamtree.util.relaxationprogress.js utility to show the progress of the process. Please see the source code of the relaxation-progress.html example page for usage information.

Certain data structures listed below are common to many methods and options of FoamTree API.

Individual group selector

Resolves to one and only one of the groups provided in the dataObject. A number of read-only options, such as geometry, require an individual group selector as an argument.

Individual groups can be designated by:

  • Group identifier, but only if identifiers are provided in the dataObject:

    console.log(foamtree.get("geometry", "1"));
  • Group object reference:

    var groups = foamtree.get("dataObject").groups;
    console.log(foamtree.get("geometry", groups[0]));

Multiple group selector

Resolves to zero or more of the groups provided in the dataObject. Many options and methods, such as selection, require a multiple group selector as a value.

Multiple groups can be designated by:

  • Individual group selector:

    foamtree.set("selection", "1");
  • Array of individual group selectors. Both id- and reference-based selectors can be mixed in one array:

    var groups = foamtree.get("dataObject").groups;
    foamtree.set("selection", [ "1", groups[1] ]);
  • Object with the groups property containing an individual group selector or an array of selectors. Certain methods, such as select, use a number of additional properties provided in the object, such as selected or keepPrevious.

    foamtree.set("selection", { groups: "1" });
    var timeout = window.setTimeout(function() {
      foamtree.set("selection", { groups: "2", keepPrevious: true });
    }, 1500);
  • Special object { all: true } that resolves to all groups from the dataObject:

    foamtree.set("selection", { all: true });

    Also the special object can contain additional properties interpreted by specific methods:

    foamtree.set("selection", { groups: "1" });
    var timeout = window.setTimeout(function() {
      foamtree.set("selection", { all: true, selected: false });
    }, 1500);
  • The null or undefined value that resolve to an empty group array.

    foamtree.set("exposure", { groups: "1" });
    var timeout = window.setTimeout(function() {
      foamtree.set("exposure", null);
    }, 1500);

Event listener function

You can register listener functions to receive notifications of various FoamTree events. Depending on the type of the listener, the function may be called with one or more parameters. Please see the documentation for the specific event for a list of parameters.

In case of low-level interaction events, such as onGroupClick, the listener will be called with one parameter – the event details object. It is possible to prevent the default behaviour associated with that interaction (group selection in case of the onGroupClick event). To prevent the default behaviour, return false or call the event.preventDefault() method in any of the listeners registered for the event.

In the context of all listeners, the this variable is a reference to the involved FoamTree instance.

Event details object

Low-level interaction events, such as onGroupClick, will receive an event object as the first parameter. The object contains the following properties:

type
the type of the event, such as click or dragstart
group
a reference to the data object corresponding to the group over which the event was triggered
topmostClosedGroup
an alias to the group property
bottommostOpenGroup
a reference to the data object corresponding to the closest open parent of the group over which the event was triggered
x, y
coordinates of the point over which the event was triggered. The coordinates are relative to the visualization container element. Use those coordinates to, for example, show a popup dialog near the mouse pointer.
xAbsolute, yAbsolute
Since 3.4.4 coordinates of the point over which the event was triggered, relative to the visualization area. These coordinates are in the same space as the one used to draw content in the groupContentDecorator and are independent of zoom, pan and expose transformations. You can use these coordinates to make custom content interactive.
secondary
true if the event was triggered by the right mouse button or a two-finger gesture on touch devices.
touches
the number of touches involved in the gesture. On non-touch devices, always equal to 1.
scale
the scale factor associated with the event. Meaningful only for the onGroupTransform and onGroupTransformEnd events, for other events the scale is 1.
delta
the direction in which the mouse wheel was rotated. Positive values mean the upwards rotation, negative values mean downwards rotation. Meaningful only for the onGroupMouseWheel event, equal to 0 for other events.
ctrlKey
true if the Ctrl key was pressed during the event
altKey
true if the Alt key was pressed during the event
metaKey
true if the meta key was pressed during the event
shiftKey
true if the Shift key was pressed during the event
preventDefault
a function that can be called to prevent the default FoamTree action, such as group selection on mouse click, associated with the event.
preventOriginalEventDefault
a function that can be called to prevent the default browser's action, such as page scrolling on mouse wheel, associated with the original event.
allowOriginalEventDefault
a function that can be called to allow the default browser's action associated with the original event. The default behaviour of FoamTree is to prevent browser's actions for the mouse wheel and touch events to make it possible to use them for visualization interactions. You can restore the default browser's actions by calling this method as shown in the scrollable content example.

For the description of the event objects of non-low-level events, please see the documentation of the specific event.

Promise

JavaScript promises are an elegant way of getting access to the result of an action that completes asynchronously at some point in future. A very simple example of such a result is the animation completing a certain time after it was initiated.

FoamTree methods that complete asynchronously, such as expose, return a promise: a JavaScript object with the then method. Using that method you can register one or more callbacks that will be called when the future action completes.

For examples of promises in use, please see the descriptions of the open and expose methods.

get(*)

get()

Returns an object containing current values of all options. Properties of the returned object correspond to option names, values are option values.

console.log(foamtree.get());
get(option)

Returns the current value of the requested option. If the provided string does not correspond to any option name, the result is undefined.

console.log(foamtree.get("dataObject"));
get(option, args...)

Returns the current value of the requested read-only option, parameterized by the provided args. The example below retrieves information about the geometry of the group with id 1. The second argument causes FoamTree to return full geometry data, including coordinates of the polygon's vertices.

console.log(foamtree.get("geometry", "1", true));

set(*)

set(option, value)

Sets the provided option to the desired value. If the provided option string does not correspond to any option, the call is ignored.

foamtree.set("exposure", "1");
set(options)

Sets new values for all options included in the provided options object. Properties of the object should correspond to attribute names, values of the object will be treated as values to set. Any properties of the options object that do not correspond to any attributes of the visualization will be ignored.

foamtree.set({
  exposure: "1",
  selection: "1",
  groupSelectionOutlineColor: "red"
});
Heads up!

For the changes of visual options to take effect, you will need to explicitly call the redraw method:

foamtree.set({
  rainbowStartColor: "#f00",
  rainbowEndColor: "#aa0"
});
foamtree.redraw();

on(type, listener)

Registers a listener for a FoamTree event. As opposed to using the set method, the on method preserves the previously registered listeners.

The on method requires two parameters:

type

A string that specifies which event to listen to. To get type string to use with this method, take the event option name, remove the leading "on" and lower-case the first letter. For example, the type string corresponding to the onGroupSelectionChanged event is groupSelectionChanged.

listener

The event listener function to invoke when the event is triggered.

The following examples registers to receive selection events and exposes the groups that have just been selected. Additionally, the code registers another listener that prevents the default interaction (expose) on double click. Notice that the this variable is a reference to the FoamTree instance inside the callback function and can be used to invoke other methods.

foamtree.on("groupSelectionChanged", function (event) {
  // "this" is the reference to FoamTree instance here.
  // Expose the groups that have just been selected.
  this.expose(event.groups);
});

// Prevent the default action (expose) on double click
foamtree.on("groupDoubleClick", function (event) {
  event.preventDefault();
});

Please see the Visualization events section for more code examples and the Events section for a list of all available events.

off(type, listener)

Removes the requested listener from the list of listeners of the specified type. As opposed to using the set method, this method will preserve the other listeners registered for the event.

The off method requires two parameters:

type

A string that specifies which event to remove the listener from. To get type string to use with this method, take the event option name, remove the leading "on" and lower-case the first letter. For example, the type string corresponding to the onGroupSelectionChanged event is groupSelectionChanged.

listener

The event listener function to remove.

The following example defines the once function that notifies the listener only about the first occurrence of the event. After the listener gets called, the function removes itself from the list of listeners using the off method, so that the listener is not called when further events of this type get triggered. Notice how the selection change listener registered using the once function gets called only for the first selection change.

var once = function (type, listener) {
  foamtree.on(type, function onceListener() {
    this.off(type, onceListener);
    return listener.apply(this, arguments);
  });
};

once("groupSelectionChanged", function (event) {
  window.console.log("Selection changed for the first time. Further selection changes will not call this function.")
});

redraw(animated, groups)

Triggers a complete redraw of the visualization. If one or more visual options of the visualization have been changed using the set method, the changes will not be visible until the visualization is redrawn, either explicitly using this method or in response to user actions.

This method accepts the following parameters:

animated
(optional) if true, FoamTree will assume that this redraw is part of an animation and may be followed by further redraws. In such cases, depending on the size of the data, FoamTree may switch to the wireframe rendering mode. The default value of this parameter is false.
groups
Since 3.1.0 (optional) a suggestion which groups need redrawing. If defined, instead of redrawing all groups, FoamTree may redraw only the groups indicated by the provided individual or multiple groups selector. Please note that in certain cases, such as when the layout of the groups changes, FoamTree may still redraw all groups and not just the ones provided in this parameters. Default value of this parameter is undefined, which means FoamTree will always redraw all groups.

The cost of the redraw operation depends on the size of the model and the specific values of certain options. Please see the Visualization rendering performance section for more details.

resize()

If the size of the HTML container element has changed, resizes and redraws the visualization to accommodate to the new size. See the Resizing section for code snippets related to this function.

If the relaxationVisible option is set to true, calling this method will trigger a complete relaxation of the layout to accommodate it to the new container dimensions. As with the initial relaxation, the resize-triggered relaxation follows the settings provided by the relaxationMaxDuration and relaxationQualityThreshold options.

The following example makes the relaxation visible, try resizing the browser window to see how relaxation is started again after the container dimensions change.

foamtree.set({
  fadeDuration: 1500,
  dataObject: {
    groups: (function() {
      var arr = [];
      for (var i = 0; i < 100; i++) {
        arr.push({
          label: "",
          weight: 0.1 + Math.random() +
                  (Math.random() < 0.2 ? Math.random() * 3 : 0)
        });
      }
      return arr;
    })()
  },

  relaxationInitializer: "random",
  relaxationVisible: true,
  relaxationQualityThreshold: 0,
  relaxationMaxDuration: 8000
});

update(groups)

Updates the visualization to reflect changes in group weights. If you change the values of the weight property of some group, you need to call this method to see the updated visualization.

This method accepts one optional parameter:

groups
(optional) if an individual or multiple group selector is provided, only the weights of the groups contained in the specified groups will be updated.

While updating the visualization, FoamTree will respect the relaxation and weight growing options. For example, to see the process of visualization update, you can set relaxationVisible to true. Additionally, you ma want to set groupGrowingDuration to a non-zero value to animate the weight transition.

Note: currently, only updating weights of existing groups is supported. Hierarchy changes, such as adding or removing groups, cannot be performed using this method. The only way to visualize an updated hierarchy is to set a new value for the dataObject option.

The following example increases a group's weight on left-click and decreases the weight on right-click. Additionally, the example sets relaxationVisible to true and groupGrowingDuration to 1000 to animate the weight transitions.

foamtree.set({
  fadeDuration: 1500,
  dataObject: largeDataSet,

  // Make relaxation and group size changes animated
  relaxationVisible: true,
  relaxationQualityThreshold: 0.5,
  relaxationMaxDuration: 8000,
  groupGrowingDuration: 1000,

  // Modify group's weight on click
  onGroupClick: function (e) {
    e.preventDefault();

    // On left-click, increase the weight,
    // decrease on right click.
    e.group.weight *= (e.secondary ? 0.5 : 1.5);

    // Update the visualization
    foamtree.update();
  }
});

The following example updates weights of the child groups of the left-clicked group. Note that only the layout of the child group changes, the top-level polygons remain unchanged.

foamtree.set({
  fadeDuration: 1500,
  dataObject: largeDataSet,

  // Make relaxation and group size changes animated
  relaxationVisible: true,
  relaxationQualityThreshold: 0.5,
  relaxationMaxDuration: 8000,
  groupGrowingDuration: 1000,

  // Modify group's weight on click
  onGroupClick: function (e) {
    e.preventDefault();

    // Change the weights of child groups
    if (e.group.groups) {
      e.group.groups.forEach(function (g) {
        g.weight *= Math.random() + 0.5;
      });
    }

    // Update weights only inside the affected group
    this.update(e.group);
  }
});

select(groups)

Changes the group selection. The groups parameter must be a multiple group selector designating the groups to select or deselect:

  • If the selector is an individual selector or an array of individual selectors, the designated groups will be selected, the selection of other groups will remain unaffected.

    foamtree.select("1");
    var timeout = window.setTimeout(function() {
      foamtree.select("2");
    }, 1500);
  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    selected
    If true or not provided, the designated groups will get selected. If false, the designated groups will be deselected.
    keepPrevious
    If true or not provided, only the selection of the designated groups will be altered. If false, the selection of the designated groups will be altered, the other groups will get unselected.

    There are two ways to clear the selection:

    foamtree.select(["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.select({ groups: [], keepPrevious: false });
    }, 2000);

    or:

    foamtree.select(["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.select({ all: true, selected: false });
    }, 2000);

Note: when selection is changed using this method, the event listeners will not be called.

expose(groups)

Changes the set of exposed groups. The groups parameter must be a multiple group selector designating the groups to expose or unexpose:

  • If the selector is an individual selector or an array of individual selectors, the designated groups will be exposed, all other groups will get unexposed.

    foamtree.expose("1");
    var timeout = window.setTimeout(function() {
      foamtree.expose("2");
    }, 2000);

    The following example shows how to unexpose all groups.

    foamtree.expose(["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.expose([]);
    }, 2000);

    A similar effect could be achieved using the reset method.

  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    exposed
    If true or not provided, the designated groups will get exposed. If false, the designated groups will be unexposed.
    keepPrevious
    If true, only the exposure of the designated groups will be altered. If false or not provided, the exposure of the designated groups will be altered, the other groups will get unexposed.

    The following example uses the keepExpose property of the group selector to expose one more group in addition to the already exposed ones. Notice that if the parent group of the group to be exposed is closed (groups 3 and 4 in the example), the parents will be automatically opened as part of the exposure process.

    foamtree.expose(["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.expose({ groups: ["3.1", "4.1"], keepPrevious: true });
    }, 2000);

The expose method returns a promise that gets resolved when the exposure animation completes. The following example exposes one of the groups by calling the expose method and registers two callbacks to be notified when the animation completes. To make the result easier to observe, we increase the duration of the expose animation a bit.

foamtree.set("exposeDuration", 2000);
var timeout = window.setTimeout(function() {
  foamtree.expose(foamtree.get("dataObject").groups[0])
    .then(function () {
      window.console.log("Expose animation complete");
    })
    .then(function () {
      window.console.log("Another callback");
    });
}, 1000);

Note: when exposure is changed using this method, the event listeners will not be called.

Note: there are some limitations when relaxation is visible.

open(groups)

Changes the set of open groups. The groups parameter must be a multiple group selector designating the groups to open or close:

  • If the selector is an individual selector or an array of individual selectors, the designated groups will get open, the state of the other groups will remain unchanged.

    foamtree.open("1");
    var timeout = window.setTimeout(function() {
      foamtree.open("2");
    }, 2000);
  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    open
    If true or not provided, the designated groups will get open. If false, the designated groups will be closed.
    keepPrevious
    If true or not provided, only the state of the designated groups will be altered. If false, the state of the designated groups will be altered, the other groups will get closed.

    To close all groups, you can use the selector with the all property:

    foamtree.open(["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.open({ all: true, open: false });
    }, 2000);

The open method returns a promise that gets resolved when the state transition animation completes. The following example opens one of the groups by calling the open method and registers two callbacks to be notified when the animation completes. To make the result easier to observe, we increase the duration of the opening animation a bit.

foamtree.set("openCloseDuration", 2000);
var timeout = window.setTimeout(function() {
  foamtree.open(foamtree.get("dataObject").groups[0])
    .then(function () {
      window.console.log("Open animation complete");
    })
    .then(function () {
      window.console.log("Another callback");
    });
}, 1000);

Note: when open state is changed using this method, the event listeners will not be called.

zoom(group)

Zooms the visualization to show the specified group. The group parameter must be an individual group selector designating the group to zoom to. You can also zoom to the dataObject group, in which case the whole visualization area will be shown.

The speed and easing of the zoom transition are controlled by the zoomMouseWheelDuration and zoomMouseWheelEasing options. The zoom method returns a promise that gets resolved when the zooming animation completes.

The following example replaces the default action performed on double click (expose) with plain zooming into the group.

foamtree.set({
  dataObject: { groups: randomGroups(50) },
  relaxationVisible: true,
  fadeDuration: 1000,
  onGroupDoubleClick: function(event) {
    event.preventDefault();
    this.zoom(event.secondary ? this.get("dataObject") : event.group);
  }
});

reset()

Resets the visualization view back to the initial state, which includes: unexposing and closing all groups, resetting the zoom factor to make the whole visualization area visible. The selection state is not affected by this method.

foamtree.select([ "1", "2" ]);
foamtree.expose([ "3", "4" ]);
foamtree.open([ "2", "3" ]);
var timeout = window.setTimeout(function() {
  foamtree.reset();
}, 3000);

trigger(type, event)

Triggers the FoamTree action associated with the event of the provided type. The most common use case for this method is replacing FoamTree's built-in touch event detection infrastructure with the code of your own.

The trigger method requires two parameters:

type

string identifier of the event to trigger. Below is a list of supported events along with the FoamTree actions they trigger:

click
a single mouse click event, selects or deselects a group
doubleclick
a double mouse click event, exposes or unexposes a group
hold
click-and-hold event, opens or closes a group
mousedown
stops zoomed visualization panning
mousewheel
turning of the mouse wheel up or down, zooms in or out the visualization
hover
hovering with the mouse pointer over the group, highlights the group
dragstart
starts panning the zoomed visualization
drag
pans the zoomed visualization
dragend
depending on the speed of panning: continues panning of the visualization or stops panning and corrects the view port in such a way that the edge of the visualization touches the edge of the viewport.
transformstart
starts the touch-based zooming of the visualization
transform
touch-based zooming of the visualization
transformend
completes the touch-based zooming of the visualization, corrects the view port in such a way that the edge of the visualization touches the edge of the viewport. If the last transformation scale was lower than 0.8 and the zooming was performed with three or more fingers, triggers reset of the visualization view.
event
the details of the event to trigger. The object must contain all the properties of the low-level event, apart from the preventDefault() method.
Heads up!

Initiating events using the trigger method will notify all the involved event listeners. For example, triggering the click event will invoke the onGroupClick listeners and, if the default action is not prevented by that listener, the onGroupSelectionChanging and the onGroupSelectionChanged events.

For this reason, calling the trigger method in event listeners may lead to the visualization falling in an endless loop.

attach(groups, maxLevels)

Initializes internal data structures for the provided groups. The groups parameter must be a multiple group selector. The maxLevels parameter indicates how many group levels to attach, assuming that the group being attached is on level 0.

You can combine this method with the maxGroupLevelsAttached option to improve the initial loading responsiveness when visualizing deeply-nested data sets with large number of groups on the top level. The general approach in this case is the following:

  1. In the onModelChanging event handler, set maxGroupLevelsAttached to 1. This will cause FoamTree to initialize only the top-level groups when new data is loaded.

  2. In the onRolloutComplete event handler, bring back maxGroupLevelsAttached to the default or desired value. Then, call the attach() method for each top level group. Make sure to allow the main browser's thread to progress by, for example, making one attach() call per one requestAnimationFrame() callback.

The attach() method returns the number of child groups for which layout has been computed.

A complete example of the above technique is available in the Deferred layout of child groups demo.

dispose()

Stops all running animations and removes all HTML event listeners registered by FoamTree. If you are repeatedly instantiating and disposing of FoamTree instances on the same page, make sure you call the dispose method when appropriate. Otherwise, dangling event listeners will prevent large chunks of memory from being garbage-collected.

Embedding

supported

A static property on CarrotSearchFoamTree equal to true if visualization is supported on the current browser environment.

Heads up!

This property is static and must be accessed directly on CarrotSearchFoamTree class, as shown in this example:

if (CarrotSearchFoamTree.supported) {
  // Put embedding code here.
  console.log("Visualization supported.");
} else {
  // Display a warning or skip visualization.
  console.log("Visualization not supported.");
}

id

Identifier of the DOM element into which the visualization is to be embedded.

Heads up!

The DOM element must have a non-zero width and height before embedding. The visualization will allocate a canvas of exactly the same size (client size) and will stretch proportionally when the element's size changes. The visualization must be resized manually (see resize method) to update to new element's dimensions.

element

The DOM element into which the visualization is to be embedded. Please see the id option for additional considerations.

Data

dataObject

The data to visualize. The provided value must be an object or null / undefined. The object will be assumed to represent the root group and must contain a non-empty groups property pointing to an array of objects representing individual first-level groups. A null or undefined object clears the visualization.

Each group can contain the following properties:

id
(optional, String) unique identifier of the group. Group identifiers are required only for programmatic changes of certain group attributes such as selection, exposure or open state.
label

(required, String) textual description of the group. For best results, use short labels.

FoamTree will handle the following special unicode characters in the label text:

Unbreakable space (\u00a0)
line breaks will not be made on the unbreakable space character
Soft hyphen (\u00ad)

when label needs to be broken into multiple lines, soft hyphen will allow a line break, generating the hyphen character at the end of the line. A soft hyphen will not add any spacing between the characters that surround it.

If you'd like to have your labels hyphenated, you can use an external hyphenation utility, such as Hyphenator.js to insert soft hyphens into your labels.

Zero-width space (\u200b)
when label needs to be broken into multiple lines, zero-width space will allow a line break. A zero-width space will not add any spacing between the characters that surround it.
New line (\n)
Since 3.4.0 forces a line break
weight

(optional, Number >= 0) weight of the group relative to other groups. The larger the weight, the more space the group's polygon will occupy on the screen. Good values for the weight property could be e.g. the number of documents in a cluster or the score of the cluster.

Group weights must be non-negative. Zero-weight groups can receive special treatment, see the showZeroWeightGroups option. If a group's weight is not specified, FoamTree will assume the weight is 1.0.

groups
(optional, Array) an array of subgroups of the group.
open
(optional, boolean) if true, the group will get open right after the new data is set.
exposed
(optional, boolean) if true, the group will get exposed right after the new data is set. This can be useful to visually highlight a certain group (or groups) as the data is loaded. Please note the limitations when relaxation is visible.
selected
(optional, boolean) if true, the group will get selected right after the new data is set.
description
Since 3.4.9 (optional, boolean) If true, descriptionGroup option is set to always and stacking is hierarchical, allocates extra space inside this group to show the group's label together with the group's child groups.
initialPosition
Since 3.4.10 (optional, object) Determines the initial position of this group. The object must contain two properties: position and distanceFromCenter. Please see the attributionPosition and attributionDistanceFromCenter options for semantics of the properties and the Initial positions demo for a complete code example.

The data object (and groups) can contain custom properties, not required or interpreted by the visualization. These properties may be used for color model tuning, for example. The following example shows a data object with various values of the above properties.

foamtree.set({
  dataObject: {
    groups: [
      { label: "Bad news", open: true, groups: [
        { label: "Last penny lost", sentiment: -0.5 },
        { label: "Bazinga doomed",  sentiment: -1 }
        ]},
      { label: "Good news", exposed: true, selected: true, groups: [
        { label: "iPads under $100",      sentiment: 0.5, selected: true },
        { label: "Martians are friendly", sentiment: 1 }
        ]},
      { label: "Other news", groups: [
        { label: "Vampires on the loose",     sentiment: -2 },
        { label: "Van Helsing to the rescue", sentiment: -3 }
        ]}
    ]
  }
});

See the Data model and Carrot2 models sections for examples of how to convert data that is not in FoamTree format.

hierarchy

Returns hierarchy-related information about a group. The returned object contains the following properties:

group
a reference to the data object representing the related group
parent
a reference to the data object representing the immediate parent of the group or null
level
the nesting level of the group. Level numbers start at 0.
index
the position of the group relative to its siblings in the input data object. Position indices start at 0.
siblingCount
the number of immediate siblings of the group
hasChildren
true if the group has any child groups
weightNormalized
the weight of the group normalized to the 0..1 range in relation the group's siblings. The raw weight of the group can be retrieved from the original data model if it was present there.
indexByWeight
Since 3.0.2 the position of the group relative to its siblings in the decreasing weight order, 0 index corresponds to the highest-weight group.
description
Since 3.4.0 true if the group represents the extra polygon holding group label in flattened stacking mode.
attribution
Since 3.3.1 true if the group is the attribution group.

To retrieve the information about a group, provide the individual group selector as the second parameter of the get method:

console.log(foamtree.get("hierarchy", "1"));

Layout

Options described in this section determine the arrangement of polygons generated by FoamTree.

layout

Determines the general type of layout to generate. Depending on this option, FoamTree will produce polygon-based organic-looking visualization or the traditional rectangular tree map.

The following values are supported:

relaxed
produces polygonal organic-looking arrangements based on Voronoi diagrams. This type of layout tends to visualize groups as near-round polygons leaving plenty of space for the group's label and content. A potential downside of this layout is that it may not very well preserve the original order of groups provided in the data object. See the relaxationInitializer option for various possible representations of the group order in the relaxed layout.
ordered
produces the traditional rectangular treemap using the strip algorithm. The advantage of this layout is that, if layoutByWeightOrder is false, it will arrange groups in the left-to-right top-to-bottom fashion according to the order they were provided in the data object. One disadvantage of this algorithm is that certain groups may be visualized as high-aspect-ratio rectangles, which will leave little useful space for the label and contents of the group. You can use the rectangleAspectRatioPreference option to tune the aspect ratios of the rectangles to some extent.
squarified
produces the traditional treemap using the squarified algorithm. This layout arranges groups along the diagonal of the container. Aspect ratios of the rectangles produced by this method tend to be lower than the ones from the ordered layout. The squarified algorithm, however, does not lay out groups in the left-to-right fashion.

The following table summarizes the possible use cases for the specific layouts.

Requirement Layout
Organic-looking layout is required. Use the relaxed layout, customize the the relaxationInitializer to choose the arrangement of polygons.
Traditional rectangular treemap is required. Use the ordered or squarified layout, customize rectangleAspectRatioPreference if needed.
The left-to-right order of groups should ideally correspond to the order in the dataObject. Use the relaxed layout, set relaxationInitializer to ordered.
The left-to-right order of groups must correspond to the order in the dataObject. Use the ordered layout, set layoutByWeightOrder to false, customize rectangleAspectRatioPreference if needed.
Close-to-round group shapes are preferred. Use the relaxed layout for a polygonal diagram or the squarified layout for a rectangular diagram.
Tip

Regardless of the layout you choose, check also the stacking option for different ways of arranging hierarchical groups.

Heads up!

Certain other options are applicable only when a specific layout is used. For example, the relaxationInitializer, relaxationVisible and other relaxation-related options are only meaningful when layout is set to relaxed.

layoutByWeightOrder

When true, FoamTree will lay out the groups in the order of decreasing weight. When false, FoamTree will lay out the groups in the order they were provided in the dataObject.

Please see the layout option for information about how the order of groups can translate into their position in the visualization.

stacking

Determines how FoamTree will display nested groups. The following values are supported:

hierarchical

produces a layered view of the hierarchy. To be able to interact with lower-level groups, the user will need to open the parent group for browsing.

This is the default stacking mode and it is particularly useful for large deeply-nested hierarchies.

See the large hierarchy demo for an application of the hierarchical stacking mode.

flattened

produces a flattened view of the hierarchy. Groups of all levels are immediately available for interaction. This kind of stacking will work especially well with a rectangular layout.

Labels of parent groups are displayed in the dedicated area (towards the top of the polygon in the screenshot). Please see the descriptionGroup* options to customize that area. You may also need to implement a groupLabelLayoutDecorator to customize the label fitting options when fitting parent group descriptions.

See the FT500 explorer, SCADA dashboard or the Unit test coverage demo for an applications of the flattened stacking mode.

Note: Currently, the flattened stacking has the following limitation: the whole group hierarchy provided initially in the dataObject property is eagerly initialized, which may block the browser for a while when a large hierarchy is provided. To avoid the delay, consider lazy initialization of the lower levels of the hierarchy.

descriptionGroup

Determines when to draw an extra group label area.

auto
The extra label area will be drawn only when stacking is set to flattened.
always

The extra label area will be drawn for all groups when stacking is set to flattened. If stacking is set to hierarchical, each group that sets its description property to true will gets its label drawn both on top of the closed group and inside the group.

foamtree.set({
  descriptionGroup: "always",
  dataObject: {
    groups: [
      {
        label: "Group with a description area", description: true,
        groups: [{}, {}, {}, {}, {}, {}]
      },
      { label: "Group without a description area", groups: [{}, {}, {}] }
    ]
  }
});

You can use the extra area to draw longer group descriptions or to mix flattened and hierarchical stacking.

See the descriptionGroupType for different description area layout styles.

descriptionGroupType

Determines how FoamTree will assign space for the group label area in flattened stacking mode or when extra description space is requested by the descriptionGroup option. Two values are currently supported:

stab
Space for the description group is reserved by cutting a horizontal strip off the top or bottom of the group's polygon.
floating
Space for the description group is reserved among the child groups, following the current layout.

Please see the groupLabelLayoutDecorator option for a way to customize the label layout for the description group.

descriptionGroupSize

Note: applicable only when stacking is set to flattened.

The desired area of the description group, relative to the area of the parent polygon. The value of 1.0 means FoamTree will try to allocate the description group to be half of the area of the polygon.

descriptionGroupMinHeight

Note: applicable only when stacking is set to flattened and descriptionGroupType is stab.

Minimum height of the description group's bounding box, in pixels. Use this option to ensure some minimum height of the description group, so that there's reasonable space to fit the label.

descriptionGroupMaxHeight

Note: applicable only when stacking is set to flattened and descriptionGroupType is stab.

Maximum height of the description group's bounding box, relative to the height of the paren group's bounding box. Use this option to ensure that the description area does not take up too much of the parent polygon's area.

descriptionGroupPosition

Note: applicable only when stacking is set to flattened.

Determines the position of the description group inside the parent polygon. The allowed values for this option depend on the current descriptionGroupType:

stab
Two values are allowed: top and bottom, which will place the description group at the top or bottom of the parent polygon, respectively.
floating
Please see attributionPosition for a description of the allowed values.

descriptionGroupDistanceFromCenter

Note: applicable only when stacking is set to flattened and descriptionGroupType is floating.

Determines the distance of the description group from the center of the parent polygon. Please see attributionDistanceFromCenter for a description of the allowed values.

showZeroWeightGroups

If true, groups whose weight is zero will be visible, with their weight slightly smaller than the smallest non-zero weight of the sibling groups.

foamtree.set({
  showZeroWeightGroups: true,
  dataObject: {
    groups: [
      { label: "Group 1", weight: 1 },
      { label: "Group 2", weight: 1 },
      { label: "Group 3", weight: 1 },
      { label: "Group 4", weight: 0 }
    ]
  }
});
foamtree.set({
  showZeroWeightGroups: false,
  dataObject: {
    groups: [
      { label: "Group 1", weight: 1 },
      { label: "Group 2", weight: 1 },
      { label: "Group 3", weight: 1 },
      { label: "Group 4", weight: 0 }
    ]
  }
});

groupMinDiameter

Minimum estimated diameter child groups must have in order to be drawn. To avoid illegible small labels and to speed up the rendering, FoamTree can be set to skip drawing child groups if their estimated diameter is smaller than groupMinDiameter. Setting the minimum group diameter to 0 will cause FoamTree to draw as many groups as possible groups given the maxGroups limit and the available floating point precision.

The following example sets a very large value for the groupMinDiameter option after a short delay. Observe that once the change is applied, the child groups disappear – their diameters were smaller than the threshold.

var timeout = setTimeout(function() {
  foamtree.set({
    groupMinDiameter: 500,

    // Set the data object again to apply the change
    dataObject: foamtree.get("dataObject")
  });
}, 2000);

maxGroups

The hard limit on the number of groups FoamTree will attempt to include in a single visualization. The limit is there to prevent excessive memory usage when browsing very deep hierarchies. The limit is applied in a breadth-first fashion, which means that if FoamTree needs to skip groups that do not fit within the limit, it will skip the groups at lower levels of the hierarchy.

rectangleAspectRatioPreference

Note: applicable only when layout is set to ordered or squarified.

Determines the desired aspect ratio of rectangles produced in the ordered and squarified layouts.

option value < 0
FoamTree will try to produce rectangles whose width is larger than the height. Such arrangement will make it slightly easier to fit labels into small rectangular groups.
option value > 0
FoamTree will prefer rectangles whose width is smaller than the height.
option value == 0
FoamTree will prefer rectangles of equal width and height.

geometry

Returns information about the geometry of the polygon representing a group or null if the group's polygon is not drawn. The returned object contains the following properties:

polygonCenterX
the X coordinate of the group's polygon. The coordinate is absolute, it does not take into account the transformations resulting from zooming, panning and exposure.
polygonCenterY
the Y coordinate of the group's polygon, absolute
polygonArea
the area of the group's polygon, absolute
boxLeft
the X coordinate of the top-left corner of the group polygon's bounding box, absolute
boxTop
the Y coordinate of the top-left corner of the group polygon's bounding box, absolute
boxWidth
width of the group polygon's bounding box, absolute
boxHeight
height of the group polygon's bounding box, absolute
labelFontSize
size of the font used to draw the group's label
labelBoxLeft
the X coordinate of the top-left corner of the group label's bounding box, absolute
labelBoxTop
the Y coordinate of the top-left corner of the group label's bounding box, absolute
labelBoxWidth
width of the group label's bounding box, absolute
labelBoxHeight
height of the group label's bounding box, absolute
polygon
an array of objects containing the X and Y coordinates of the groups polygon, in the clockwise order. All polygons produced by FoamTree are convex. The array is only returned if true is passed as the third parameter of the get method.
neighbors

Since 3.4.0 an array of references to the group objects corresponding to the neighbors of this group. The array is aligned with the polygon array: at an index corresponding to a polygon vertex, it contains the neighbor that shares the edge starting at the vertex and going clockwise. Please note that that some slots in the neighbor array may be null, which means the corresponding edge is the boundary of the visualization area. For a potential use case of this property, see the Background demo.

Note: The neighbors array is available only when the layout option is set to relaxed. The array is only returned if true is passed as the third parameter of the get method.

To retrieve the information about a group, provide the individual group selector as the second parameter of the get method.

console.log(foamtree.get("geometry", "1"));

To retrieve the full geometry information, including coordinates of the polygon's vertices, pass true as the third parameter of the get method:

console.log(foamtree.get("geometry", "1", true));

Please see the accuracy.html demo for an advanced code example that uses the geometry information to compute the accuracy with which polygon's areas correspond to the group's weights.

containerCoordinates

Converts the the provided visualization-area-relative point coordinates to the coordinates relative to the HTML container in which FoamTree is embedded. You can use this method to position additional HTML elements over specific shapes drawn inside some FoamTree group and ensure that the position will be correct regardless of zooming, panning and group exposure.

Two arguments are required to retrieve the container-relative coordinates:

  • Individual group selector, passed as the second argument to the get method, identifying the FoamTree group in which the input point lies. FoamTree group is required because transformations, such as exposure, are applied on per-group basis, so the inverse coordinate transform needs to take into account the scale and offset of the specific transformed group.
  • Visualization-area-relative coordinates of the point of interest, as an object with x and y properties, passed as the third argument to the get method. The coordinate space you will use here is the same as the on in which custom group content is drawn in groupContentDecorator.

The following example retrieves the container-relative coordinates of the center of some group.

var geometry = foamtree.get("geometry", "1");
console.log(foamtree.get("containerCoordinates", "1",
  { x: geometry.polygonCenterX, y: geometry.polygonCenterY }));

Tip: If your goal is to use this property to synchronize the position of some HTML element with a specific point inside FoamTree visualization, the best place to perform the synchronization is the onRedraw callback, which is called every time FoamTree is redrawn. This will ensure that you will have the chance to reposition the element on zoom, pan or exposure changes.

Please see the sticky-tooltips.html demo for a real-world usage of this property.

Relaxation

Options described in this section determine how FoamTree refines the initial polygon arrangement to arrive at a visually pleasing result. Options in this section are only applicable when layout is set to relaxed.

relaxationInitializer

Note: applicable only when layout is set to relaxed.

Determines the initial layout of groups' polygons. The following values are supported:

fisheye
puts large groups in the center of the visualization area and the smaller groups towards the corners
blackhole
puts small groups in the center of the visualization and the larger groups towards the corners
ordered
Since 3.3.0 lays out the groups in a left-to-right lines, in the order the groups were specified in the data object. This initializer creates layouts most similar to the ones generated by FoamTree 2.0.x.
squarified
puts large groups close to the top-left corner of the visualization and the small groups close to the bottom-right corner.
random
puts groups at random positions

The example below demonstrates the available initializers. Note that in order to apply a new initializer to the currently visible data, you need to set the dataObject option to trigger the layout update.

foamtree.set({
  dataObject: {
    groups: (function () {
      var arr = [
        { label: "fisheye", weight: 7 }, { label: "blackhole", weight: 6 },
        { label: "ordered", weight: 5 }, { label: "squarified", weight: 5 },
        { label: "random", weight: 4 }
      ];
      for (var i = 0; i < 100; i++) {
        arr.push({
          label: "",
          weight: 0.1 + Math.random() +
            (Math.random() < 0.2 ? Math.random() * 3 : 0)
        });
      }
      return arr;
    })()
  },

  relaxationVisible: true,         // show relaxation
  wireframeLabelDrawing: "always", // show labels during relaxation

  onGroupClick: function (event) {
    event.preventDefault();
    if (event.group.label) {
      this.set("relaxationInitializer", event.group.label);

      // Set the data object again to trigger layout update
      this.set("dataObject", this.get("dataObject"));
    }
  }
});

Since 3.1.0 You can also specify different layout initializers for each parent group separately by passing the initializer type in the relaxationInitializer property of the group's data object.

The following example demonstrates all of the available initializers in one visualization.

foamtree.set({
  dataObject: {
    groups: (function () {
      var initializers = [ "fisheye", "random", "ordered", "squarified", "blackhole" ];
      return initializers.map(function (i) {
        return {
          relaxationInitializer: i, // pass the initializer to use for this group
          label: i,
          groups: randomGroups(40)
        }
      });
    })()
  },

  relaxationVisible: true,         // show relaxation
  relaxationMaxDuration: 6000,     // make relaxation last longer
  relaxationQualityThreshold: 0.1,
  fadeDuration: 2000,              // use a simple fade-in
  rolloutDuration: 0,
  pullbackDuration: 0,
  wireframeLabelDrawing: "always"  // show labels during animation
});

relaxationVisible

Note: applicable only when layout is set to relaxed.

When set to true, FoamTree will show the intermediate steps of layout relaxation. For large data sets, when layout computation may take a few seconds, you may want to show the relaxation process to the users, so that they don't see the blank screen while waiting for the final visualization to be computed.

Layout relaxation and rollout animation can run in parallel. Alternatively, to let the user watch the layout stabilize, you may want to disable the rollout by setting rolloutDuration to 0 and fading in the visualization container by setting fadeDuration to 1000, for example.

foamtree.set({
  dataObject: {
    groups: (function () {
      var arr = [];
      for (var i = 0; i < 100; i++) {
        arr.push({
          label: "",
          weight: 0.1 + Math.random() +
            (Math.random() < 0.2 ? Math.random() * 3 : 0)
        });
      }
      return arr;
    })()
  },

  relaxationInitializer: "random",
  relaxationVisible: true,
  fadeDuration: 1000
});

When relaxation is visible, you can report the progress of the process to the user using the relaxation progress utility.

Heads up!

When relaxation process is visible, the API-triggered exposure changes of a group will be ignored until the polygon corresponding to that group is computed. In particular, when relaxationVisible is true, the initial exposure state provided in the dataObject will be ignored. As a workaround, you can expose the requested sites when the rollout completes as shown in the following example.

foamtree.set({
  dataObject: { groups: randomGroups(20) },
  relaxationVisible: true,
  fadeDuration: 1000,
  onRolloutComplete: function(progress, complete) {
    foamtree.expose(foamtree.get("dataObject").groups[0]);
  }
});

relaxationMaxDuration

Note: applicable only when layout is set to relaxed.

Determines the maximum duration of relaxation, in milliseconds. Depending on the complexity of the data set and the relaxationQualityThreshold, the relaxation may or may not complete before the specified timeout. If FoamTree needs to stop the relaxation because the alloted time has been exceeded, the quality of the layout may be lowered.

Heads up!

Currently, the relaxationMaxDuration is enforced only if relaxationVisible is set to true.

The following example sets a fairly short maximum relaxation duration. Observe how this lowers the quality of the layout by creating non-round, elongated polygons.

foamtree.set({
  dataObject: {
    groups: (function () {
      var arr = [];
      for (var i = 0; i < 100; i++) {
        arr.push({
          label: "",
          weight: 0.1 + Math.random() +
            (Math.random() < 0.2 ? Math.random() * 3 : 0)
        });
      }
      return arr;
    })()
  },

  relaxationInitializer: "random",
  relaxationVisible: true,
  relaxationMaxDuration: 100
});

relaxationQualityThreshold

Note: applicable only when layout is set to relaxed.

The desired layout quality to achieve during relaxation. High-quality layouts will consist of "round" polygons, lower-quality layouts will tend to contain more elongated, irregular shapes. The lower the value of the threshold, the higher the quality of the layout and the longer the layout computation time. If relaxationVisible is true, the desired quality level may not be achieved if the relaxationMaxDuration timeout is exceeded.

Setting relaxationQualityThreshold to 0 and relaxationMaxDuration to Number.MAX_VALUE will create the highest-quality layout possible, at the cost of long relaxation time. Setting relaxationQualityThreshold, regardless of the relaxation duration timeout will disable the relaxation leading to lower-quality layout, but faster computation. The following examples demonstrate the two opposite situations.

foamtree.set({
  dataObject: { groups: randomGroups(80) },
  relaxationInitializer: "fisheye",
  relaxationVisible: true,
  relaxationQualityThreshold: Number.MAX_VALUE
});
foamtree.set({
  dataObject: { groups: randomGroups(80) },
  relaxationInitializer: "fisheye",
  relaxationVisible: true,
  relaxationMaxDuration: Number.MAX_VALUE,
  relaxationQualityThreshold: 0
});

groupGrowingDuration

Note: applicable only when layout is set to relaxed.

The duration of the animation that grows the group to its final weight, in milliseconds. Applicable only when relaxationVisible is true.

foamtree.set({
  dataObject: { groups: randomGroups(80) },
  relaxationInitializer: "fisheye",
  relaxationVisible: true,
  groupGrowingDuration: 3000
});

groupGrowingEasing

Note: applicable only when layout is set to relaxed.

The easing function to use to grow group's weights. Meaningful only when groupGrowingDuration is larger than 0.

groupGrowingDrag

Note: applicable only when layout is set to relaxed.

The amount of delay to apply when growing subsequent groups on the same level of hierarchy. The delay will be computed by multiplying groupGrowingDuration by the value of this option. The first child group will start growing immediately, the second one after the computed delay, the third group will start growing after twice the delay and so on.

foamtree.set({
  dataObject: { groups: randomGroups(80) },
  relaxationInitializer: "fisheye",
  relaxationVisible: true,
  groupGrowingDuration: 2000,
  groupGrowingDrag: 0.2
});

groupResizingBudget

Note: applicable only when layout is set to relaxed.

Determines how many times FoamTree should attempt to set the desired size for the groups polygon. Depending on the characteristics of the input data, FoamTree may not be able to set the desired sizes of the polygons right away. Depending on the groupResizingBudget value, FoamTree will make several attempts to set the desired size during the course of relaxation.

Setting this option to 0 may cause large-weight groups to be rendered smaller than desired. Setting a large value of this option may lead to "jumpy" and time-consuming relaxation.

Group borders

Options in this section determine the spacing between group's polygons.

groupBorderRadius

Determines the amount of rounding to apply to polygon corners. Setting group border radius to 0 will produce straight corners, the value of 1.0 will produce near-to-oval shapes.

groupBorderWidth

Determines the width of the empty space between the sibling group polygons, in pixels.

Heads up!

Setting groupBorderWidth to a value smaller than groupStrokeWidth / 2 + 0.5 will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes.

groupBorderWidthScaling

The scaling factor to apply when drawing borders of child groups. Border with of a child group will be groupBorderWidthScaling smaller than the border width of the immediate parent group. Setting groupBorderWidthScaling to 1.0 will draw borders of equal widths on all levels of the group hierarchy.

groupInsetWidth

Determines the width of the empty space between the parent group's edge and its child groups' edges, in pixels.

groupBorderRadiusCorrection

The correction factor that ensures that the rounded parent group polygon covers all child group's polygons. For large groupBorderRadius values, it may sometimes happen that certain child polygons "stick out" of their parent group. In such cases, you can increase the groupBorderRadiusCorrection value to ensure proper rendering.

Group fill

Options documented in this section determine how FoamTree will fill the polygons.

groupFillType

Determines the way the group polygons should be filled. The following values are possible:

none
the polygons will not be filled at all
plain
the polygons will be filled with one color; fast to render
gradient
the polygons will be filled with a two-color radial gradient; slower to render
Heads up!

On slower devices and/or larger data sets, FoamTree may automatically disable rendering of gradients to improve the rendering performance. To force the rendering of gradients in such cases, increase wireframeDrawMaxDuration, finalCompleteDrawMaxDuration and finalIncrementalDrawMaxDuration.

groupFillGradientRadius

Determines the radius of the group-filling gradient.

groupFillGradientCenterHueShift

The amount of hue to add or subtract from the base group color hue to create the polygon-center end of the fill gradient.

groupFillGradientCenterSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the polygon-center end of the fill gradient.

groupFillGradientCenterLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the polygon-center end of the fill gradient.

groupFillGradientRimHueShift

The amount of hue to add or subtract from the base group color hue to create the polygon-edge end of the fill gradient.

groupFillGradientRimSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the polygon-edge end of the fill gradient.

groupFillGradientRimLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the polygon-edge end of the fill gradient.

Group stroke

Options documented in this section determine how FoamTree will draw the edges of the polygons.

groupStrokeType

Determines the way the group polygons edges should be drawn. The following values are possible:

none
the edges will not be drawn at all; fastest to render
plain
the edges will be drawn using one color; slower to render
gradient
the edges will be drawn using a two-color linear gradient; slowest to render
Heads up!

On slower devices and/or larger data sets, FoamTree may automatically disable rendering of edges to improve the rendering performance. To force the rendering of gradients in such cases, increase wireframeDrawMaxDuration, finalCompleteDrawMaxDuration and finalIncrementalDrawMaxDuration.

groupStrokeWidth

Determines the width of the group's edge, in pixels.

Heads up!

Setting groupStrokeWidth to a value larger than (groupBorderWidth - 0.5) * 2 will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes.

groupStrokePlainHueShift

The amount of hue to add or subtract from the base group color hue to create the color used for drawing the plain group's edge.

groupStrokePlainSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the color used for drawing the plain group's edge.

groupStrokePlainLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the color used for drawing the plain group's edge.

groupStrokeGradientRadius

The size of the linear gradient used to draw the polygon's edges, in relation to the polygon's size.

groupStrokeGradientAngle

The angle of the linear gradient used to draw the polygon's edges.

groupStrokeGradientUpperHueShift

The amount of hue to add or subtract from the base group color hue to create the upper-end color of the polygon edge's gradient.

groupStrokeGradientUpperSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the upper-end color of the polygon edge's gradient.

groupStrokeGradientUpperLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the upper-end color of the polygon edge's gradient.

groupStrokeGradientLowerHueShift

The amount of hue to add or subtract from the base group color hue to create the lower-end color of the polygon edge's gradient.

groupStrokeGradientLowerSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the lower-end color of the polygon edge's gradient.

groupStrokeGradientLowerLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the lower-end color of the polygon edge's gradient.

Group selection

Options documented in this section determine how FoamTree will draw and decorate the selected groups.

groupSelectionOutlineColor

Color of the outline stroke for the selected groups.

groupSelectionOutlineWidth

Width of the selection outline to draw around selected groups.

Heads up!

Setting groupSelectionOutlineWidth to a value larger than

(groupBorderWidth-groupSelectionOutlineShadowSize - 0.5) * 2

will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes when one ore more groups are selected.

groupSelectionOutlineShadowSize

The size of the drop shadow to apply to the selection outline.

Heads up!

Setting groupSelectionOutlineShadowSize to a value larger than

(groupBorderWidth-groupSelectionOutlineWidth - 0.5) * 2

will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes when one ore more groups are selected.

groupSelectionOutlineShadowColor

The color of the selection outline shadow.

groupSelectionFillHueShift

The amount of hue to add or subtract from the base fill color hue when the group is selected.

groupSelectionFillSaturationShift

The amount of saturation to add or subtract from the base fill color saturation when the group is selected.

groupSelectionFillLightnessShift

The amount of lightness to add or subtract from the base fill color lightness when the group is selected.

groupSelectionStrokeHueShift

The amount of hue to add or subtract from the base stroke color hue when the group is selected.

groupSelectionStrokeSaturationShift

The amount of saturation to add or subtract from the base stroke color saturation when the group is selected.

groupSelectionStrokeLightnessShift

The amount of lightness to add or subtract from the base stroke color lightness when the group is selected.

Group hover

Options documented in this section determine how FoamTree will highlight the hovered-on groups.

groupHoverFillHueShift

The amount of hue to add or subtract from the base fill color hue when the group is hovered on.

groupHoverFillSaturationShift

The amount of saturation to add or subtract from the base fill color saturation when the group is hovered on.

groupHoverFillLightnessShift

The amount of lightness to add or subtract from the base fill color lightness when the group is hovered on.

groupHoverStrokeHueShift

The amount of hue to add or subtract from the base stroke color hue when the group is hovered on.

groupHoverStrokeSaturationShift

The amount of saturation to add or subtract from the base stroke color saturation when the group is hovered on.

groupHoverStrokeLightnessShift

The amount of lightness to add or subtract from the base stroke color lightness when the group is hovered on.

Group labels

Options documented in this section determine how FoamTree will lay out and draw group labels.

groupLabelFontFamily

Font family to use for drawing group labels. CSS-compliant font family specifications are supported, including webfonts imported using the @font-face syntax.

groupLabelFontStyle

Font style to use for drawing group labels. All CSS-compliant font styles are allowed, such as italic.

groupLabelFontWeight

Font weight to use for drawing group labels. All CSS-compliant font weights are allowed, such as bold.

groupLabelFontVariant

Font variant to use for drawing group labels. All CSS-compliant font variants are allowed, such as small-caps.

groupLabelMinFontSize

Minimum font size used for drawing group labels, in pixels.

Note that setting minimum font size to a large value may result in many labels being replaced with an ellipsis ("dots"), as in this example:

foamtree.set({
  dataObject: largeDataSet,
  groupLabelMinFontSize: 30,
  groupLabelMaxFontSize: 40
});

To force FoamTree to draw labels for all groups, set groupLabelMinFontSize to 0. This setting, however, may significantly slow down visualization rendering.

groupLabelMaxFontSize

Maximum font size used for drawing group labels. See the groupLabelMinFontSize attribute for details.

groupLabelDecorator

Allows to customize or completely replace the default text of group labels. When labels are drawn, FoamTree will call the provided function once for each group (parent groups will be processed before children groups). The task of the function is to modify or completely replace the default label text. The custom label text will most likely be based on custom properties passed along with the data model.

The signature of the decorator callback should be the following:

function (options, properties, variables)

where:

options
all current visualization options (keys and values).
properties

an object with properties describing the group being decorated. The object will be a union of objects retrieved from the hierarchy, state and geometry options.

variables

object with label-related variables this function can change.

The following variables are available:

labelText
the default text of the label, equal to the label property of the group's data object.
Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

The following example upper cases all group and adds angle quotes around them. Note how the quotes are separated from the main label by the non-breakable space unicode to ensure the quotes are not left as the only characters in the line. See the documentation for the dataObject option for a list of other special characters supported by FoamTree.

foamtree.set({
  groupLabelDecorator: function(opts, props, vars) {
    vars.labelText = "«\u00a0" + vars.labelText.toLocaleUpperCase() + "\u00a0»";
  }
});

groupLabelLineHeight

The line height to use when rendering labels in multiple lines.

groupLabelHorizontalPadding

The padding to add on the left and right of the label, in current font size unit.

groupLabelVerticalPadding

The padding to add on the top and bottom of the label, in current font size unit.

groupLabelMaxTotalHeight

The maximum total height of the label in relation to the height of the group's polygon.

groupLabelUpdateThreshold

Every time the geometry of the group's polygon changes, FoamTree needs to decide whether to re-layout the label text. The groupLabelUpdateThreshold determines how much must the area of the polygon change for FoamTree to re-layout the label. For example, if the value of the threshold is 0.05, FoamTree will re-layout the label when the area of the polygon is larger or smaller by 5% or more.

Setting groupLabelUpdateThreshold to 0 will cause FoamTree to re-layout the label on every group polygon change. Depending on the size of the data set, it may significantly slow down visualization rendering when relaxationVisible is true.

groupLabelColorThreshold

Picks the label color automatically depending on the group's color brightness. If the brightness is below this threshold groupLabelLightColor is used, otherwise groupLabelDarkColor is used. Setting the threshold to either 0 or 1 will pick dark or light labels, correspondingly.

Automatic choice of label colors is also provided as part of groupColorDecorator's functionality.

groupLabelDarkColor

The label color to use on bright groups (see groupLabelColorThreshold).

groupLabelLightColor

The label color to use on dark groups (see groupLabelColorThreshold).

Group exposure

Options described in this section determine how FoamTree draws and decorates exposed groups.

groupExposureScale

The magnification scale at which exposed groups will be drawn. If you set groupExposureScale to 1.0, exposed groups will be drawn at the same scale as the unexposed groups.

groupExposureShadowColor

The color of drop shadow to apply to exposed groups.

groupExposureShadowSize

The size of drop shadow to apply to exposed groups, in pixels.

groupExposureZoomMargin

The margin size to apply when zooming to the exposed group, relative to the group polygon's bounding box dimensions. If groupExposureZoomMargin is 0, the exposed group will occupy the full height / width of the view port. For larger values, a zoom margin will be added around the exposed group. The larger the value, the larger margin will be applied.

groupUnexposureLightnessShift

The amount of lightness to add or subtract from the groups base color's lightness when the group is unexposed.

groupUnexposureSaturationShift

The amount of saturation to add or subtract from the groups base color's saturation when the group is unexposed.

groupUnexposureLabelColorThreshold

The labelColorThreshold value to use for unexposed groups. When the colors of unexposed groups are significantly modified using the groupUnexposureLightnessShift and groupUnexposureSaturationShift options, you may want to modify this threshold to, for example, always draw the labels of unexposed groups in the dark color.

exposeDuration

The duration of the group exposure and unexposure animation.

exposeEasing

The easing function to use during group exposure and unexposure animations. Meaningful only when exposeDuration is larger than 0.

Group opening/closing

openCloseDuration

The duration of the group opening or closing animation.

Group hierarchy

Options in this group determine how the stacked polygons representing the hierarchy of groups will be drawn.

parentFillOpacity

The opacity to use when filling groups that have child groups. By default, the parent group is drawn semi-transparent to indicate that the group has child groups that can be browsed. If you set parentFillOpacity to 1.0, the parent group will be fully opaque and the child groups will not be visible until the group gets open. If you set parentFillOpacity to 0.0, the parent group's polygon will not be filled at all and the child groups will be fully visible at all times.

parentStrokeOpacity

The opacity to use when drawing strokes of groups that have child groups.

parentLabelOpacity

The opacity to use when drawing labels of groups that have child groups.

parentOpacityBalancing

When set to true, FoamTree will try to equalize the opacity of groups' polygons, so that the aggregated opacity of, for example, a group with 3 levels of child groups and a group without child groups will be roughly the same. Opacity equalization will usually lead to a more uniform look of the whole visualization area.

Opacity balancing will only be applied when the base color of the group is not fully opaque, that is when its alpha component is less than 1.

Group colors

This section describes options that determine the base colors assigned for groups.

rainbowStartColor

Start color to use if rainbow color model is used for coloring groups. The rainbow color model will radially or linearly spread the color hue among top-level groups, starting at rainbowStartColor and ending at rainbowEndColor. Sub-groups will be painted with the parent group's hue but with varying degrees of saturation and lightness.

For custom coloring of groups, use groupColorDecorator.

rainbowEndColor

End color to use if rainbow color model is used for coloring groups. See rainbowStartColor for a description of how the rainbow color model works.

rainbowColorDistribution

Determines how the rainbow colors will be distributed. You can use one of the following values:

radial
distributes the colors radially around the center of the visualization area
linear
distributes the colors linearly across the whole visualization area

You can use the rainbowColorDistributionAngle option to further tune the color distribution.

rainbowColorDistributionAngle

When rainbowColorDistribution is radial, determines the angle at which the rainbow color will start. Change the the values of this option, to rotate the rainbow around the central point of the visualization area.

When rainbowColorDistribution is linear, determines the angle of the linear gradient formed by the rainbow color transition.

rainbowLightnessDistributionAngle

Determines the angle of the linear gradient formed by the color lightness variations created for lower-level groups.

rainbowSaturationCorrection

When rainbowColorDistribution is radial, FoamTree can desaturate colors of the polygons lying close to the center of the visualization area to make the color transitions smoother.

You can use the rainbowLightnessCorrection option to vary the strength of this correction. The value of 0 will moderately decrease the saturation, the value of -1 will strongly desaturate the center groups. Finally, set the correction to 1 to disable correction decreases completely.

rainbowLightnessCorrection

When rainbowColorDistribution is radial, FoamTree can adjust the lightness of colors of the polygons lying close to the center of the visualization area to make the color transitions smoother.

You can use the rainbowLightnessCorrection option to vary the strength of this adjustment. The value of 0 means no adjustment. The value of -1 will strongly decrease the lightness of the center groups. Finally, the value of 1 will strongly increase the lightness of the central groups.

rainbowLightnessShift

Determines the strength of the color lightness variations applied to lower-level groups.

rainbowLightnessShiftCenter

Determines the central lightness value around which the lightness variations will be applied for the lower-level groups.

groupColorDecorator

A callback function you can use to customize or completely replace the default rainbow color model for groups. During each redraw, the visualization code will call the provided function once for each group. The task of the function is to modify or completely replace the default color of the group and its label. New colors can be derived from various properties of the group, such as its nesting level, number of siblings, or custom properties passed along with the data model.

The signature of the decorator callback should be the following:

function (options, properties, variables)

where:

options
all current visualization options (keys and values).
properties
an object with properties describing the group being decorated. The object will be a union of objects retrieved from the hierarchy, state and geometry options.
variables

an object with two properties: groupColor, containing the group color computed by the default rainbow color model, and labelColor always equal to auto. The callback function can either change some of the properties of groupColor or replace any of the colors with a new object representing the color to use.

The callback function can replace these values with a CSS3 color string (hsl, hsla, rgb or rgba color specification) or an object containing the following properties:

  • model: rgba for the RGBA model, hsla for the HSLA model. When adding or changing the values of other properties, it is important to set this property to reflect the desired color model.
  • r, g, b, a: RGBA values (integers in the 0..255 range).
  • h, s, l, a: HSLA values, hue is an angle (0..359), saturation and lightness are percentages (0..100) and transparency is a range between 0 and 1.

The default values computed by the rainbow color model are always passed to the callback in the HSLA format described above.

The labelColor variable can be set to a string value of auto which will recompute the label color again depending on the updated groupColor and visualization options controlling dark/ light label colors: labelColorThreshold, labelLightColor and labelDarkColor.

Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

Colors defined in the data model

The simplest color model could just use explicit color values provided as part of the data model.

foamtree.set({
  dataObject: {
    groups: [
      { label: "C", color: "#00aeef" },
      { label: "M", color: "#ec008c" },
      { label: "Y", color: "#fff200" },
      { label: "K", color: "#231f20" }
    ]
  },
  groupColorDecorator: function (opts, params, vars) {
    vars.groupColor = params.group.color;
    vars.labelColor = "auto";
  }
});

Colors depending on group state

By default, FoamTree modifies the base group color depending on the state of the group. This behaviour is determined by such properties as groupSelectionStrokeLightnessShift or groupHoverFillLightnessShift and usually makes the selected or hovered-on group lighter. To achieve even greater customization, you can use the group state information in the decorator:

foamtree.set({
  dataObject: {
    groups: [
      { label: "C", color: "#00aeef" },
      { label: "M", color: "#ec008c" },
      { label: "Y", color: "#fff200" },
      { label: "K", color: "#231f20" }
    ]
  },
  groupColorDecorator: function (opts, params, vars) {
    if (params.selected) {
      vars.groupColor = "#fff";
      vars.labelColor = params.group.color;
    } else {
      vars.groupColor = params.group.color;
      vars.labelColor = "#fff";
    }
  }
});

Colors derived from custom properties of the data model

Most commonly, polygon colors will depend on some domain-specific properties of the groups. For instance, if FoamTree is used to visualize a group of news stories and each story has been tagged with sentiment information (positive, negative, neutral), the color could reflect the sentiment. In the example shown below, we draw positive news in green, negative in red and neutral in grey.

foamtree.set({
  groupColorDecorator: function(opts, params, vars) {
    // Sentiment is a custom property with values in the -1..+1 range.
    // -1 means negative sentiment, +1 -- positive, 0 -- neutral.
    var sentiment = params.group.sentiment;
    if (sentiment === undefined) {
      // Make neutral groups grey
      vars.groupColor.s = 0;
      vars.groupColor.l = 0;
      vars.groupColor.a = 0.2;
    } else {
      if (sentiment > 0) {
        // Make positive groups green
        vars.groupColor.h = 120;
      } else {
        // Make negative groups red
        vars.groupColor.h = 0;
      }

      // Make saturation and lightness depend on
      // the strength of the sentiment.
      vars.groupColor.s = 50 * (1 + Math.abs(sentiment));
      vars.groupColor.l = Math.abs(60 * sentiment);
      vars.groupColor.a = 0.5;
    }
    // Indicate that we use the HSLA model
    vars.groupColor.model = "hsla";
  },
  dataObject: {
    groups: [
      { label: "Bad news", groups: [
        { label: "Last penny lost", sentiment: -0.5 },
        { label: "Bazinga doomed",  sentiment: -1 }
        ]},
      { label: "Good news", groups: [
        { label: "iPads under $100",      sentiment: 0.5 },
        { label: "Martians are friendly", sentiment: 1 }
        ]},
      { label: "Other news", groups: [
        { label: "Vampires on the loose", sentiment: 0 },
        { label: "Van Helsing to the rescue", sentiment: 0 }
        ]}
    ]
  }
});

Animated colors

Finally, you can create an animated color model by making the color decorator a time-dependant function and periodically redrawing the visualization using the redraw method. The example below creates a simple radar-like animation. For faster drawing, we set groupFillType to plain and groupStrokeType to none.

var frame = 0;
var running = true;
foamtree.set({
  groupColorDecorator: function(opts, params, vars) {
    // We change the color only for top-level groups. For child groups,
    // FoamTree will generate brightness variations of the parent group's color.
    if (params.level == 0) {
      var delay = 5;
      var count = 3;
      var frameDiv = Math.floor(frame / delay);
      var mod = frameDiv % params.siblingCount;
      var diff = (mod - params.index + params.siblingCount)
                   % params.siblingCount;
      if (diff < count) {
        vars.groupColor.g = 255 *
          (1 - ((diff * delay + frame % delay) / (delay * count)));
      } else {
        vars.groupColor.g = 40;
      }
      vars.groupColor.r = 40;
      vars.groupColor.b = 40;
      vars.groupColor.model = "rgb";
    }
  },
  dataObject: {
    groups: (function() {
      var arr = [];
      for (var i = 0; i < 50; i++) {
        arr.push({
          label: "",
          weight: Math.pow(Math.random(), 3) * 0.8 + 0.2,
          groups: [ { label: "" }, { label: "" }, { label: "" }]
        });
      }
      return arr;
    })()
  },
  groupFillType: "plain",
  groupStrokeType: "none"
});

// Redraw the visualization periodically. Although we use setTimeout()
// here, use the requestAnimationFrame() when possible.
setTimeout(function redraw() {
  if (!running) { // stopped?
    return;
  }
  foamtree.redraw(true);
  frame++;
  setTimeout(redraw, 16); // next frame
}, 16);

Rollout

You can use options described in this section to change the animation FoamTree uses to show a new data set. By changing various rollout properties you can create many different animation styles.

rolloutStartPoint

The point in the visualization container area at which the rollout will start. The following start point values are allowed:

center
rollout will start from the group lying closest to the center of the visualization container
topleft
rollout will start from the group lying closest to the top-left corner of the visualization container
bottomright
rollout will start from the group lying closest to the bottom-right corner of the visualization container
random
rollout will start from a random group

rolloutMethod

FoamTree will start the rollout animation from the group determined using the rolloutStartPoint option. Then, further polygons will be included in the animation, depending on the value of this option:

groups

on each animation step, all neighbors of the groups already being animated will be included in the animation, which will create a sort of ripple effect as shown in the following example.

foamtree.set({
  dataObject: { groups: randomGroups(200) },

  // Roll out in groups
  rolloutMethod: "groups",

  // Create a gentle scaling effect
  rolloutDuration: 6000,
  rolloutEasing: "bounce",
  rolloutScalingStrength: -0.4,
  rolloutRotationStrength: 0,
  rolloutPolygonDrag: 0.08
});
individual

on each animation step only one neighbor of the groups already being animated will be included in the animation. When using this rollout method, you may want to decrease rolloutPolygonDrag to make the animation progress smoother.

foamtree.set({
  dataObject: { groups: randomGroups(200) },

  // Roll out each group individually
  rolloutMethod: "individual",

  // Create a gentle scaling effect
  rolloutDuration: 4000,
  rolloutEasing: "bounce",
  rolloutScalingStrength: -0.4,
  rolloutRotationStrength: 0,
  rolloutPolygonDrag: 0.015
});

rolloutDuration

The duration of the rollout animation at one level of the hierarchy, in milliseconds. The duration of the complete animation is variable and depends on the number of hierarchy levels and the rolloutChildGroupsDrag and rolloutChildGroupsDelay options.

rolloutEasing

The animation easing function to use during rollout animation.

rolloutScalingStrength

Determines the initial scale to set for each polygon at the start of the rollout. During the rollout animation the initial scale will be transitioned to the neutral value.

When rolloutScalingStrength is -1, the initial scale will be minus infinity making the polygon appear at infinite distance in front of the user. When rolloutScalingStrength is 1, the initial scale will be plus infinity making the polygon appear at infinite distance behind the user. All intermediate values are allowed and will set the initial scale to the appropriate proportion between the extremes. When rolloutScalingStrength is 0, the initial scale will be equal to the neutral scale.

The following examples demonstrate the rollout animation for negative and positive values of the rolloutScalingStrength option. Both examples set rolloutTransformationCenter to 0 so that the origin of the scaling is the center of the visualization container.

foamtree.set({
  dataObject: { groups: randomGroups(100) },

  rolloutScalingStrength: -0.5,

  rolloutDuration: 4000,
  rolloutRotationStrength: 0,
  rolloutTransformationCenter: 0
});
foamtree.set({
  dataObject: { groups: randomGroups(100) },

  rolloutScalingStrength: 1,

  rolloutDuration: 4000,
  rolloutRotationStrength: 0,
  rolloutTransformationCenter: 0
});
Heads up!

When visualizing hierarchical models, setting a positive rolloutScalingStrength may create unpleasant effects because child groups will be drawn on top of their parents during the animation as shown in the following example. For this reason, you may want to use negative scaling strengths for hierarchical data.

foamtree.set({
  dataObject: largeDataSet,

  rolloutScalingStrength: 0.5,

  rolloutDuration: 4000,
  rolloutRotationStrength: 0,
  rolloutTransformationCenter: 0
});

rolloutTranslationXStrength

Determines the initial horizontal translation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial translation to the neutral value. The unit of the translation is the width of the parent polygon's bounding box. Fox example, the value of -0.5 means the translation to the left by half of the width of the parent box.

rolloutTranslationYStrength

Determines the initial vertical translation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial translation to the neutral value. The unit of the translation is the height of the parent polygon's bounding box. Fox example, the value of 0.5 means the translation to the bottom by half of the height of the parent box.

rolloutRotationStrength

Determines the initial rotation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial rotation to the neutral value. Rotation of 1.0 means 180 degrees clockwise.

rolloutTransformationCenter

Determines the point relative to which the rollout transformations will be taken. The value of 0.0 means the center of the parent group's polygon, 1.0 means the center of the currently animated polygon. Intermediate values set the transformation center at the appropriate linear combination of the above extremes.

The following examples demonstrate the difference between the rolloutTransformationCenter extremes.

foamtree.set({
  dataObject: { groups: randomGroups(100) },

  rolloutTransformationCenter: 0,

  rolloutDuration: 4000,
  rolloutRotationStrength: -0.3,
  rolloutScalingStrength: 0,
  rolloutEasing: "squareInOut"
});
foamtree.set({
  dataObject: { groups: randomGroups(100) },

  rolloutTransformationCenter: 1,

  rolloutDuration: 4000,
  rolloutRotationStrength: -0.3,
  rolloutScalingStrength: 0,
  rolloutEasing: "squareInOut"
});

rolloutPolygonDrag

The amount of delay to apply before starting the rollout of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying rolloutDuration by the value of this option.

rolloutPolygonDuration

Determines the duration of the polygon rollout animation, as a fraction of rolloutDuration.

rolloutLabelDelay

Determines the delay to apply before showing the group's label during rollout, as a fraction of rolloutDuration.

Note that when wireframeLabelDrawing is set to never, group labels will not be drawn at all during rollout. When wireframeLabelDrawing is set to auto, labels may or may not be drawn during the rollout, depending on the performance of the device and the size of the data set. To force FoamTree to draw labels during the rollout animation, set wireframeLabelDrawing to always.

rolloutLabelDrag

The amount of delay to apply before revealing the labels of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying rolloutDuration by the value of this option.

rolloutLabelDuration

Determines the duration of the label fading-in animation, as a fraction of rolloutDuration.

rolloutChildGroupsDelay

The amount of delay to apply before starting the rollout of child groups of a parent group, as a fraction of rolloutDuration. If you set rolloutChildGroupsDelay to 0, groups at all levels will start their rollout animations at the same time.

rolloutChildGroupsDrag

The amount of delay to apply before starting the rollout of child groups of subsequent parent groups groups on the same level of hierarchy. The delay will be computed by multiplying rolloutDuration by the value of this option.

Pullback

You can use options described in this section to change the animation FoamTree uses to hide the current data set before showing a new one. By changing various rollout properties you can create many different animation styles. Many of the pullback options are similar to their rollout counterparts.

pullbackStartPoint

The point in the visualization container area at which the pullback will start. The following start point values are allowed:

center
pullback will start from the group lying closest to the center of the visualization container
topleft
pullback will start from the group lying closest to the top-left corner of the visualization container
bottomright
pullback will start from the group lying closest to the bottom-right corner of the visualization container
random
pullback will start from a random group

pullbackMethod

FoamTree will start the pullback animation from the group determined using the pullbackStartPoint option. Then, further polygons will be included in the animation, depending on the value of this option:

groups

on each animation step, all neighbors of the groups already being animated will be included in the animation, which will create a sort of ripple effect as shown in the following example.

// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(200) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    // Roll out in groups
    pullbackMethod: "groups",

    // Create a gentle scaling effect
    pullbackDuration: 3000,
    pullbackEasing: "quadOut",
    pullbackScalingStrength: -0.4,
    pullbackRotationStrength: 0,
    pullbackPolygonDrag: 0.08
  });
}, 1000);
individual

on each animation step only one neighbor of the groups already being animated will be included in the animation. When using this rollout method, you may want to decrease rolloutPolygonDrag to make the animation progress smoother.

// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(200) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    // Roll out in groups
    pullbackMethod: "individual",

    // Create a gentle scaling effect
    pullbackDuration: 3000,
    pullbackEasing: "quadOut",
    pullbackScalingStrength: -0.4,
    pullbackRotationStrength: 0,
    pullbackPolygonDrag: 0.015
  });
}, 1000);

pullbackDuration

The duration of the pullback animation, in milliseconds.

pullbackEasing

The animation easing function to use during pullback animation.

pullbackScalingStrength

Determines the target scale for each polygon to achieve at the end of the pullback animation.

When pullbackScalingStrength is -1, the target scale will be minus infinity making the polygon appear at infinite distance in front of the user. When pullbackScalingStrength is 1, the target scale will be plus infinity making the polygon appear at infinite distance behind the user. All intermediate values are allowed and will set the target scale to the appropriate proportion between the extremes. When pullbackScalingStrength is 0, the target scale will be equal to the neutral scale.

The following examples demonstrate the pullback animation for negative and positive values of the pullbackScalingStrength option. Both examples set pullbackTransformationCenter to 0 so that the origin of the scaling is the center of the visualization container.

// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(200) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    pullbackScalingStrength: -0.5,

    pullbackDuration: 2500,
    pullbackEasing: "quadIn",
    pullbackRotationStrength: 0,
    pullbackTransformationCenter: 0
  });
}, 1000);
// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(200) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    pullbackScalingStrength: 0.5,

    pullbackDuration: 2500,
    pullbackEasing: "quadOut",
    pullbackRotationStrength: 0,
    pullbackTransformationCenter: 0
  });
}, 1000);

pullbackTranslationXStrength

Determines the target horizontal translation for each polygon to achieve at the end of the pullback animation. The unit of the translation is the width of the parent polygon's bounding box. Fox example, the value of -0.5 means the translation to the left by half of the width of the parent box.

pullbackTranslationYStrength

Determines the target vertical translation for each polygon to achieve at the end of the pullback animation. The unit of the translation is the height of the parent polygon's bounding box. Fox example, the value of 0.5 means the translation to the bottom by half of the height of the parent box.

pullbackRotationStrength

Determines the target rotation for each polygon to achieve at the end of the pullback animation. Rotation of 1.0 means 180 degrees clockwise.

pullbackTransformationCenter

Determines the point relative to which the pullback transformations will be taken. The value of 0.0 means the center of the parent group's polygon, 1.0 means the center of the currently animated polygon. Intermediate values set the transformation center at the appropriate linear combination of the above extremes.

The following examples demonstrate the difference between the pullbackTransformationCenter extremes.

// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(100) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    pullbackTransformationCenter: 0,

    pullbackDuration: 4000,
    pullbackRotationStrength: -0.3,
    pullbackScalingStrength: 0,
    pullbackEasing: "cubicInOut"
  });
}, 1000);
// Set initial data
foamtree.set({
  dataObject: { groups: randomGroups(100) }
});

// Pull back after a while
var timeout = setTimeout(function() {
  foamtree.set({
    dataObject: null,

    pullbackTransformationCenter: 1,

    pullbackDuration: 4000,
    pullbackRotationStrength: -0.3,
    pullbackScalingStrength: 0,
    pullbackEasing: "cubicInOut"
  });
}, 1000);

pullbackPolygonDelay

The pullback animation will first start hiding groups labels and after a delay the hiding of the polygons will start. This option determines the delay to apply before hiding of the polygons starts. The delay is specified as a fraction of pullbackDuration.

pullbackPolygonDrag

The amount of delay to apply before starting the pullback of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying pullbackDuration by the value of this option.

pullbackPolygonDuration

Determines the duration of the polygon pullback animation, as a fraction of pullbackDuration.

pullbackLabelDelay

Determines the delay to apply before hiding group labels during pullback, as a fraction of pullbackDuration.

pullbackLabelDrag

The amount of delay to apply before hiding the labels of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying pullbackDuration by the value of this option.

pullbackLabelDuration

Determines the duration of the label fading-out animation, as a fraction of pullbackDuration.

pullbackChildGroupsDuration

Determines the duration of the sub-group fading-out animation, as a fraction of pullbackDuration.

pullbackChildGroupsDelay

Determines the delay to apply before hiding sub-groups during pullback, as a fraction of pullbackDuration.

pullbackChildGroupsDrag

The amount of delay to apply before hiding the subgroups of subsequent parent groups on the same level of hierarchy. The delay will be computed by multiplying pullbackDuration by the value of this option.

Fading

When rolloutDuration or pullbackDuration is 0, FoamTree will use a simple fade in/out animation to show and hide the visualization. You can use options described in this section to customize the fading.

fadeDuration

The duration of the fade-in/out animation, in milliseconds. The fading in/out is applied only when rolloutDuration / pullbackDuration is 0, otherwise the value of this option is ignored. Set this option to 0 to disable fading.

fadeEasing

The easing function to use when performing the fading animation.

Zoom

Options in this section customize the way the visualization can be zoomed using the mouse wheel.

zoomMouseWheelFactor

The magnification factor to apply when the user zooms in using the mouse wheel. When zooming out, the factor will be 1 /zoomMouseWheelFactor.

zoomMouseWheelDuration

The duration of the zoom-in/out animation, in milliseconds.

zoomMouseWheelEasing

The easing function to apply to the zoom animation.

Title bar

titleBarFontFamily

Font family for the title bar, if shown. CSS-compliant font family specifications are supported, including webfonts imported using the @font-face syntax. If not specified, the same font as specified for the groupLabelFontFamily will be used.

This example renders the title bar in monospace font.

foamtree.set({
  dataObject: { groups: [
    { label: "ABC123", selected: true },
    { label: "DEF456" }]},
  titleBar: "inscribed",
  titleBarFontFamily: "monospace",
  maxLabelSizeForTitleBar: Number.MAX_VALUE,
  groupLabelFontFamily: "Arial, sans-serif"
});

titleBarFontStyle

Font style for the title bar, if shown. CSS-compliant font style specifications are supported, such as italic. If not specified, groupLabelFontStyle will be used.

titleBarFontWeight

Font weight for the title bar, if shown. CSS-compliant font weight specifications are supported, such as bold. If not specified, groupLabelFontWeight will be used.

titleBarFontVariant

Font variant for the title bar, if shown. CSS-compliant font variant specifications are supported, such as small-caps. If not specified, groupLabelFontVariant will be used.

titleBarMinFontSize

Minimum font size to use when drawing the title bar's label, in pixels.

Note that setting minimum font size to a large value may result in an empty title bar.

titleBarMaxFontSize

Maximum font size to draw the title bar's label, in pixels.

titleBarBackgroundColor

The background color of the title bar area.

titleBarTextColor

The text color for drawing labels in the title bar area.

titleBarTextPaddingLeftRight

Left and right (horizontal) padding to leave inside the title bar's area.

titleBarTextPaddingTopBottom

Top and bottom (vertical) padding to leave inside the title bar's area.

titleBarDecorator

Allows to customize or completely replace the text displayed in the title bar. By default, FoamTree will display the group's label, but you can use this option to display a different text, e.g. some more details related to the group.

The decorator will be called every time the user's pointer hovers over a group. The signature of the decorator callback should be the following:

function (options, properties, variables)

where:

options
all current visualization options (keys and values).
properties

an object with properties describing the group the user is hovering over. The object will be a union of objects retrieved from the hierarchy, state and geometry options.

Additionally, the following title-bar-specific properties are available:

titleBarWidth
the width of the title bar area, in pixels
titleBarHeight
the height of the title bar area, in pixels
labelFontSize
the font size used to draw the actual group label or 0 if the label is not drawn
viewportScale
Since 3.1.0 the current viewport scale. Values larger than 1 mean the viewport is zoomed-in, values smaller than 1 mean the viewport is zoomed-out, a value of 1 means the viewport is at its neutral zoom level.
variables

object with title-bar-related variables this function can change.

The following variables are available:

titleBarText
the default text for the title bar, equal to the label of the group. If changed to null or undefined, the title bar will not be shown.
titleBarShown
true if the group's label is small enough for the title bar to show, false otherwise. You can change the value of this variable to force showing or hiding of the title bar.
titleBarMaxFontSize
the maximum font size to be used for drawing text in the title bar, by default equal to the titleBarMaxFontSize option. Change this variable to override the the max font size for the currently rendered title bar.
Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

The following example shows how to use the title bar decorator to display more details about the group. A similar complete example can be found in titlebar-details.html.

foamtree.set({
  maxLabelSizeForTitleBar: Number.MAX_VALUE,

  titleBarDecorator: function(options, parameters, variables) {
    variables.titleBarText = "More details about " + parameters.group.label;
  }
});

The following example shows how to force showing of the title bar for specific groups.

foamtree.set({
  titleBarDecorator: function(options, parameters, variables) {
    var groupId = parameters.group.id;
    if (groupId === "1" || groupId === "2") {
      variables.titleBarShown = true;
    }
  }
});

maxLabelSizeForTitleBar

Maximum group label size in pixels whose label to show in the title bar. Small size of a group may cause its label to be drawn in a small font and thus appear illegible. For this reason, when the user hovers the mouse pointer over a group with a small label, the label can be shown in a dedicated title bar.

The maxLabelSizeForTitleBar defines the threshold below which group labels will be shown in the title bar. Setting maxLabelSizeForTitleBar to 0 will disable the title bar completely. Setting the option to Number.MAX_VALUE will cause the title bar to appear for all groups.

Attribution

attributionText

The label of the attribution group. If attributionText or attributionLogo is not empty, an extra group is added to the visualization with the provided text and logo. If attributionUrl is not empty, clicking the attribution group will open the provided URL in the browser.

Heads up!

The limited demo version of FoamTree does not allow to disable or change the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

attributionLogoScale

Determines the scale at which FoamTree should draw the attribution logo. The scale is relative to the bounding box of the group's polygon. A scale of 1.0 means the logo will take the whole available width or height.

Heads up!

The limited demo version of FoamTree does not allow to change the scale of the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

attributionUrl

The URL to open when the user clicks the attribution group, can be undefined.

Heads up!

The limited demo version of FoamTree does not allow to disable or change the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

attributionWeight

Sets the weight of the attribution group in relation to the total weight of the root-level groups. You can use this option to influence the size of the attribution group.

Heads up!

The limited demo version of FoamTree does not allow to set the attribution weight to a value smaller than 0.0025. Please contact Carrot Search for licensing of a fully customizable distribution.

attributionPosition

Determines the position of the attribution group relative to the visualization container. The following values are supported:

topleft
top-left corner of the visualization container
topright
top-right corner of the visualization container
bottomleft
bottom-left corner of the visualization container
bottomright
bottom-right corner of the visualization container
top
top side of the visualization container
bottom
bottom side of the visualization container
left
left side of the visualization container
right
right side of the visualization container
random
a random point on the perimeter of the visualization container
number in the [0, 360) range
The angle (in degrees) that determines position on the perimeter of the visualization container, clockwise, starting at the right hand side of the container. For example: 0 is the right-hand side of the container, 45 is the bottom-right corner, 90 is the bottom side of the container etc.

Note: When layout is ordered or squarified, only the topleft and bottomright attribution group placement is supported. All other values will result in the group being placed in the bottom-right corner of the visualization area.

Note: when there is a small number of groups in the visualization, FoamTree may not be able to place the attribution group exactly at the required point.

The following example places the attribution group in the top-right corner of the visualization.

foamtree.set("attributionPosition", 360 - 45);
foamtree.set("dataObject", { groups: randomGroups(25, 1) } );

attributionDistanceFromCenter

Note: applicable only when layout is set to relaxed.

Determines the distance of the attribution group from the center of the visualization area. The distance of 0 puts the attribution group in the center of the visualization, the distance of 1 puts the attribution group near the perimeter of the visualization area.

Please note that when there is a small number of groups in the visualization, FoamTree may not be able to place the attribution group exactly at the required point.

The following example attempts to place the attribution group near the center of the visualization area.

foamtree.set("attributionDistanceFromCenter", 0);
foamtree.set("dataObject", { groups: randomGroups(25, 1) } );

attributionTheme

Determines the color of the attribution group. Currently, two values are supported:

light
Attribution group will be drawn in a light color.
dark
Attribution group will be drawn in a dark color.

Rendering

pixelRatio

The physical-to-display pixel count ratio to assume when drawing the final visualization. On modern devices with high-density screens (such as the Retina display) one logical pixel can be mapped to more than one physical pixel. You can use this option to increase the resolution at which the visualization is drawn to make the image clearer and labels more legible. You can also decrease this resolution to make rendering faster at the cost of quality.

By default, pixelRatio is 1. In such cases, the width and height of the canvas on which the visualization is drawn will be equal to the pixel dimensions of the HTML container element. For pixelRatio values greater/smaller than 1, the pixel dimensions of the canvas will be smaller/larger than the dimensions of the enclosing HTML element and the canvas will be stretched to fit in the container's client area. For example, if you set pixelRatio to 2 and if the size of the enclosing HTML element is 400x400 pixels, the size of the canvas will be 800x800 pixels. Such a configuration will better utilize the extra pixels available on Retina displays, for example.

On most modern browsers you can retrieve the device-specific pixel ratio from the window.devicePixelRatio property.

If the pixel ratio is changed after FoamTree has initialized, you will need to call redraw for the change to be applied.

The following examples demonstrate the difference between a pixel ratio higher and lower than the pixel resolution.

foamtree.set({
  dataObject: largeDataSet,
  pixelRatio: 0.5
});
foamtree.redraw();
foamtree.set({
  dataObject: largeDataSet,
  pixelRatio: 2
});
foamtree.redraw();
Heads up!

To boost the performance on iPads with Retina display set pixelRatio to 2 and the width of the HTML container element to be at least one pixel less than the total screen width. Yes, we know it's weird but it speeds up rendering a lot.

When setting pixelRatio to a value larger than 1 on mobile devices, make sure to correctly set the size of the page's viewport. The default viewport may be very large, leading to a very large canvas. Refer to the mobile demo's source code for examples.

wireframePixelRatio

The pixel ratio to use when drawing animations, such as rollout, pullback, expose or zooming. You can change the default to a higher value (e.g. 2 on Retina displays) to have the animation drawn in more detail, but noticeably slower. You can also set this option to a lower value, e.g. 0.5 to have the animation drawn faster, but with less detail. Please see the pixelRatio option for detailed considerations on how the pixel ratio value affects the sizes of the canvases allocated by FoamTree.

groupContentDecorator

A callback function you can use to customize or completely replace the way group polygons' content is drawn. The task of the function is to render the contents by invoking the appropriate drawing methods on the provided drawing context that behaves like the standard CanvasRenderingContext2D.

The signature of the decorator callback should be the following:

function (options, properties, variables)

where:

options
all current visualization options (keys and values).
properties

an object with properties describing the group being rendered. The object will be a union of objects retrieved from the hierarchy, state and geometry options.

Additionally, a number of shape-decorator-specific properties are available.

variables

object with a number of variables this decorator can change.

The following variables are available:

groupLabelDrawn
set to false to skip rendering of the default group's label; true by default.
groupPolygonDrawn
set to false to skip rendering of the polygon corresponding to the group; true by default.
Group content decorator specific properties

The properties object will contain a number of additional properties useful for drawing custom content in FoamTree polygons.

context

a drawing context that behaves like the standard CanvasRenderingContext2D. The decorator should call the appropriate drawing methods on this object to render the contents of the polygon.

The provided object is not an actual canvas drawing context, but a buffer that records all the invocations so that FoamTree can replay them when needed without invoking the decorator, for example during zooming or panning. The groupContentDecoratorTriggering option determines when this decorator will be triggered and when the last buffered content will be "replayed" without invoking the decorator.

The drawing context buffer offers a number of additional methods not available in a standard drawing context, such as drawing rounded rectangles or filling a polygon with text.

shapeDirty

indicates whether the geometry of the group's polygon has changed since the last invocation of the decorator. If the custom layout is expensive to compute, you may want to cache it and recompute the layout only when the shapeDirty property is true.

This property is meaningful only when groupContentDecoratorTriggering is onSurfaceDirty. If the triggering is done onShapeDirty, the shapeDirty property will true on all invocations of the decorator.

viewportScale

the current viewport scale. Values larger than 1 mean the viewport is zoomed-in, values smaller than 1 mean the viewport is zoomed-out, a value of 1 means the viewport is at its neutral zoom level.

Using this property makes sense only when groupContentDecoratorTriggering is onSurfaceDirty, in which case you may want to vary the amount of detail drawn depending on the zoom level.

polygonContext

Since 3.4.0 a read-only context buffer containing the drawing commands that set the path corresponding to the group's polygon. You can replay() this buffer to the drawing context, so that later you can fill(), stroke() or clip() the polygon's shape. In most cases you'll set the groupPolygonDrawn variable to false to prevent FoamTree from drawing the default polygon.

Use cases of this buffer range from drawing custom fill or stroke around the group polygon to filling the polygon with a pattern or an image. In tandem with the labelContext property, you can use it to alter the default order of drawing group elements. See the Photo explorer or the SCADA dashboard demos for real-world applications.

The following example uses the polygonContext property to draw custom gradient fills based on a 0..1 value associated with each group.

foamtree.set({
  dataObject: { groups: randomGroups(30) },
  onModelChanging: function (dataObject) {
    dataObject.groups.forEach(function (group) {
      group.value = 0.2 + Math.random() * 0.8
    })
  },
  groupContentDecorator: function(opts, props, vars) {
    var group = props.group;
    var value = group.value;
    if (!value) {
      return;
    }

    // Compute the geometry of the gradient
    var boundingBox = CarrotSearchFoamTree
        .geometry.boundingBox(props.polygon);
    var ctx = props.context;
    var gradient = ctx.createLinearGradient(
        boundingBox.x, boundingBox.y + boundingBox.h,
        boundingBox.x, boundingBox.y);

    // Compute base color based on the value
    var hue = (-10 + 110.0 * (1 - 2 * Math.abs(value - 0.5)));
    var baseColor = "hsla(" + hue.toFixed(1) + ", "
        + (90 + (props.hovered ? 10 : 0)) + "%, "
        + (40 + (props.hovered ? 20 : 0)) + "%, 1.0)";

    // Set up color steps
    var th = 0.05;
    gradient.addColorStop(0, baseColor);
    if (value > th) {
      gradient.addColorStop(value - th, baseColor);
    }
    gradient.addColorStop(value, "rgba(255, 255, 255, 0.5)");
    if (value + th < 1) {
      gradient.addColorStop(value + th, "rgba(255, 255, 255, 0.5)");
    }

    // Prevent default drawing of the polygon
    vars.groupPolygonDrawn = false;

    // Set up polygon path
    ctx.beginPath();
    props.polygonContext.replay(ctx);
    ctx.closePath();

    // Fill with custom gradient
    ctx.fillStyle = gradient;
    ctx.fill();

    // Stroke
    ctx.strokeStyle = "hsla(" + hue.toFixed(1) + ", "
        + (90 + (props.hovered ? 10 : 0)) + "%, "
        + (30 + (props.hovered ? 20 : 0)) + "%, 1.0)";
    ctx.stroke();
  }
});
labelContext

Since 3.4.0 a read-only context buffer containing the commands that draw the default group label. You can replay() this buffer to the drawing context to actually draw the label.

Use cases of this buffer range from drawing the label with custom fill or transformation to altering the default order of drawing group elements.

The following example uses the labelContext property to draw slightly rotated default labels. Please note that label fitting is still performed in the non-rotated setting, so the rotation may cause some labels to stick outside of their polygons.

foamtree.set({
  dataObject: largeDataSet,
  groupContentDecorator: function(opts, props, vars) {
    vars.groupLabelDrawn = false;
    var ctx = props.context;
    ctx.translate(props.polygonCenterX, props.polygonCenterY);
    ctx.rotate(-Math.PI / 16);
    ctx.translate(-props.polygonCenterX, -props.polygonCenterY);
    props.labelContext.replay(ctx);
  }
});
Tips & caveats

Please note the following tips and caveats when implementing your drawing code:

  • The coordinate space in which all drawing happens is determined by the coordinates of the group's polygon. The coordinates of the polygon are available in the polygon property. You may want to apply a suitable transformation to the drawing context to avoid complex coordinate arithmetics.
  • To make the custom content interactive, you can use the onGroupMouseMove event along with the visualization-relative coordinates. These coordinates correspond to the ones you'll use to draw the custom content. See the Interactive custom content demo for some example code.
  • FoamTree exposes a number of geometry utilities that may be useful when implementing the shape decorator code.
  • FoamTree does not restrict the drawing area to the area of the polygon being filled. This means that it is the responsibility of your code not to overrun the polygon boundaries. If your code draws beyond the boundaries of the polygon, you will be seeing artifacts resulting from incremental redraws and arbitrary order of polygon drawing.

    One option is to use the clip method of the drawing context to restrict the drawing area, but this will severely impact the drawing performance. A much faster variant would be, for example, to restrict drawing to an inscribed rectangle computed using the rectangleInPolygon method.

  • Due to performance optimizations applied by FoamTree, such as incremental redraws, your code must not make any assumptions about the order in which this decorator will be invoked. If the code needs to precompute any global information, the best place to do so would be the onRedraw or onModelChanged event listeners. See also the groupContentDecoratorTriggering option.
  • Each group's elements are drawn in the following order:

    1. group's polygon (unless the decorator sets the groupPolygonDrawn variable to false)
    2. the shapes drawn by the decorator
    3. the default group's label (unless the decorator sets the groupLabelDrawn variable to false)
  • If the group has a non-empty label, the custom content will only be drawn when the label is visible. When the current viewport scale is too small for the label to be drawn, the custom content will not be drawn and the label-too-small dots will appear instead. If, however, the group does not have a label, custom content will always be drawn. In this case, this decorator may further decide whether to draw any content depending on the size of the polygon and the current viewport scale.
Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

Code examples

The following example shows how to use the group shape decorator to draw a two-way pie chart that can express some additional information about the group.

foamtree.set({
  rolloutDuration: 3000,
  dataObject: largeDataSet,

  // Our custom drawing depends on the group state,
  // therefore we need to use the "onSurfaceDirty" triggering.
  groupContentDecoratorTriggering: "onSurfaceDirty",

  // Our custom drawing is pretty fast, so we can draw it during animations
  wireframeContentDecorationDrawing: "always",

  // Our custom drawing callback
  groupContentDecorator: function (opts, params, vars) {
    var ctx = params.context;
    var centerX = params.polygonCenterX;
    var centerY = params.polygonCenterY;

    // Compute pie chart radius
    var radius = CarrotSearchFoamTree.geometry.circleInPolygon(
      params.polygon, centerX, centerY) * 0.8;

    // Increase opacity of the pie chart on hover
    var baseAlpha = (params.hovered ? 2 : 1);

    // Increase stroke width on selection,
    // use thinner lines on lower hierarchy levels
    ctx.lineWidth = (params.selected ? 3.0 : 1.5) * Math.pow(0.5, params.level);

    // The black part of the pie chart
    ctx.beginPath();
    ctx.moveTo(centerX, centerY);
    ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI * params.group.rank);
    ctx.closePath();
    ctx.strokeStyle = rgba("0, 0, 0", baseAlpha * 0.2);
    ctx.stroke();
    ctx.fillStyle = rgba("0, 0, 0", baseAlpha * 0.1);
    ctx.fill();

    // The white part of the pie chart
    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, 2 * Math.PI * params.group.rank, 0);
    ctx.strokeStyle = rgba("255, 255, 255", baseAlpha * 0.2);
    ctx.stroke();
    ctx.lineTo(centerX, centerY);
    ctx.fillStyle = rgba("255, 255, 255", baseAlpha * 0.2);
    ctx.fill();

    function rgba(rgb, alpha) {
      return "rgba(" + rgb + ", " + alpha + ")";
    }
  },

  // Generates some random data we'll display in the pie chart
  onModelChanged: function (data) {
    enhance(data);

    function enhance(group) {
      group.rank = 0.1 + Math.random() * 0.9;
      if (group.groups) {
        group.groups.forEach(enhance);
      }
    }
  }
});

The following example shows how to draw some extra information below the default label.

foamtree.set({
  dataObject: largeDataSet,

  // Our custom drawing callback
  groupContentDecorator: function (opts, params, vars) {
    var ctx = params.context;
    var centerX = params.polygonCenterX;

    // We want to put the extra label inside a semi transparent rectangle.
    // To do this, we need to draw the rectangle first and then the label.
    // The dimensions of the rectangle are returned by the fillPolygonWithText()
    // method, so to get the right drawing order, we need to draw the text
    // to a scratch buffer, draw the rectangle and then draw the text we
    // saved in the scratch buffer.
    var scratch = ctx.scratch();

    var info = scratch.fillPolygonWithText(
      // Fit the extra text inside the main polygon
      params.polygon,

      // Fit the extra text below the main label
      centerX, params.labelBoxTop + params.labelBoxHeight,

      // use non-breakable space to prevent line breaks
      params.group.weight + "\u00a0docs",

      {
        maxFontSize: 0.6 * params.labelFontSize, // restrict max font size
        verticalAlign: "top", // flow the text downwards from the center point
        verticalPadding: 0.1, // use some smaller than default padding
        maxTotalHeight: 1 // use the full available height
      });

    // Draw the rectangle. FoamTree already set for us the color
    // it used to draw the label, we'll use that color.
    if (info.fit) {
      var labelBox = info.box;
      var boxMargin = labelBox.h * 0.1;
      ctx.roundRect(labelBox.x - 2 * boxMargin, labelBox.y - boxMargin,
        labelBox.w + 4 * boxMargin, labelBox.h + 2 * boxMargin, boxMargin * 2);

      ctx.globalAlpha = 0.15;
      ctx.fill();
      ctx.globalAlpha = 0.25;
      ctx.lineWidth = boxMargin * 0.3;
      ctx.stroke();

      // Draw the text from our scratch buffer
      ctx.globalAlpha = 1.0;
      scratch.replay(ctx);
    }
  }
});

See also the images demo for code that renders SVG images inside FoamTree polygons and the GitHub search demo for more complex custom content rendering.

groupContentDecoratorTriggering

Determines when the groupContentDecorator will be triggered:

onShapeDirty

The shape decorator will only be triggered if the shape of the group's polygon changes, for example during the relaxation process or after resizing of the visualization area. During redraws in which the shape of the polygon is not changing, such as when zooming or drawing selection outline, FoamTree will "replay" the custom drawing commands issued by the last invocation of the shape decorator, which will improve the rendering performance.

Use this triggering mode when the custom content of the group does not depend on the state of the group (selected, hovered) and thus the group shape decorator does not need to be called when group state changes.

onSurfaceDirty

The shape decorator will be triggered every time the group's polygon needs to be drawn. Use this triggering mode if your custom content drawing depends on the state of the group (selected, hovered). Depending on the complexity of your custom drawing code, using this triggering mode may result in lower rendering performance.

wireframeLabelDrawing

Determines whether FoamTree should draw group labels during animation. The following values are allowed:

auto
FoamTree will try to automatically enable or disable drawing of labels based on the performance of the browser and the size of the data set to meet the limit set in wireframeDrawMaxDuration.
always
labels will always be drawn during animations, regardless of the wireframeDrawMaxDuration limit.
never
labels will never be drawn during animations, regardless of the wireframeDrawMaxDuration limit.

wireframeContentDecorationDrawing

Determines whether FoamTree should draw the group content decorations produced by the groupContentDecorator during animation. The following values are allowed:

auto

FoamTree will try to automatically enable or disable drawing of group content decorations based on the performance of the browser and the size of the data set to meet the limit set in wireframeDrawMaxDuration.

For the purposes of this calculation FoamTree will assume that the custom shapes drawn by the groupContentDecorator are as expensive to draw as labels. If the custom drawing does not contain any text and/or is fast to draw, you can set this option to always.

always
group content decorations will always be drawn during animations, regardless of the wireframeDrawMaxDuration limit.
never
group content decorations will never be drawn during animations, regardless of the wireframeDrawMaxDuration limit.

groupLabelLayoutDecorator

A callback function you can use to customize how the label of a specific group is laid out, including the font, size and paddings.

The signature of the decorator callback should be the following:

function (options, properties, variables)

where:

options
all current visualization options (keys and values).
properties

an object with properties describing the group being rendered. The object will be a union of objects retrieved from the hierarchy, state and geometry options.

variables

object with a number of variables this decorator can change. The following variables are available:

fontFamily
See fontFamily in fillPolygonWithText().
fontStyle
See fontFamily in fillPolygonWithText().
fontVariant
See fontVariant in fillPolygonWithText().
fontWeight
See fontWeight in fillPolygonWithText().
lineHeight
See lineHeight in fillPolygonWithText().
horizontalPadding
See horizontalPadding in fillPolygonWithText().
verticalPadding
See verticalPadding in fillPolygonWithText().
maxTotalTextHeight
See fontFamily in fillPolygonWithText().
maxFontSize
See maxFontSize in fillPolygonWithText().

One use case for this callback is lowering label padding for description groups for flattened stacking as demonstrated by the following example.

foamtree.set({
  dataObject: largeDataSet,
  stacking: "flattened",
  groupLabelLayoutDecorator: function (opts, props, vars) {
    if (props.description) {
      // Lower padding
      vars.verticalPadding = 0.1;

      // Allow the label to take the full
      // height of the description group
      vars.maxTotalTextHeight = 1.0;

      // Render description labels in bold
      vars.fontWeight = "700";
    }
  }
});

descriptionGroupPolygonDrawn

Note: applicable only when stacking is set to flattened.

When true, the polygon corresponding to the description group will be drawn. By default, the polygon is not drawn to make an impression that the description group is linked to the parent polygon.

maxGroupLevelsDrawn

The maximum number of levels of closed groups FoamTree will draw. For hierarchies with more than maxGroupLevelsDrawn levels, the lower-level groups will not be drawn until the appropriate number of their parent groups get open. If your data set contains many levels of groups with many children on each level, you may want to lower the value of this option to speed up layout computation and rendering at the cost of lower amount of visible detail.

The following example generates a random 10-level hierarchy with 2 groups at each level, sets the initial maxGroupLevelsDrawn to 1 so that only the top-level groups are drawn. Then, once a second, the code will increment maxGroupLevelsDrawn to demonstrate that lower-level groups will indeed be drawn. Additionally, the code will gradually lower the parentFillOpacity, so that the lower-level groups are more clearly visible. Finally, the example sets groupMinDiameter to 0 so that as many groups are possible fit on the screen.

foamtree.set({
  // Lower groupMinDiameter to fit as many groups as possible
  groupMinDiameter: 0,

  // Start with drawing just the top-level groups
  maxGroupLevelsDrawn: 1,

  // Generate a 10-level hierarchy
  dataObject: { groups: randomGroups(2, 10 /* levels */) }
});

// Initiate revealing of lower levels
var timeout;
(function reveal() {
  timeout = window.setTimeout(function () {
    var levels = foamtree.get("maxGroupLevelsDrawn") + 1;
    foamtree.set("maxGroupLevelsDrawn", levels);
    foamtree.set("parentFillOpacity", foamtree.get("parentFillOpacity") * 0.8);
    foamtree.redraw();
    if (levels < 10) {
      reveal();
    }
  }, 1000);
})();

maxGroupLabelLevelsDrawn

The maximum number of levels of group labels FoamTree will draw. For hierarchies with more than maxGroupLabelLevelsDrawn levels, only the top specified number of open groups will have their labels drawn. You can lower the default value of this option for faster rendering of the visualization at the cost of lower amount of visible detail.

The following example generates and visualizes a random 10-level hierarchy with 2 groups at each level. Click and hold on the groups to open them and observe how deeper level polygons and labels get revealed.

foamtree.set({
  // Lower groupMinDiameter to fit as many groups as possible
  groupMinDiameter: 0,

  // Draw 2 levels of labels and 4 levels of group polygons
  maxGroupLevelsDrawn: 4,
  maxGroupLabelLevelsDrawn: 2,

  // Tune the border options to make them more visible
  parentFillOpacity: 0.3,
  parentStrokeOpacity: 1.0,
  groupBorderRadius: 0,
  groupBorderWidth: 3,
  groupBorderWidthScaling: 0.8,

  // Generate a 10-level hierarchy
  dataObject: { groups: randomGroups(2, 10 /* levels */) },

  // Always draw the same "label"
  groupLabelDecorator: function (opts, params, vars) {
    vars.labelText = params.level;
  }
});

maxGroupLevelsAttached

The maximum number of group levels for which to compute layout when new dataObject is set. Combined with the attach method, this option can be used to improve the responsiveness of FoamTree when visualizing deeply-nested hierarchies with large numbers of groups on the top level.

For more details and examples, see the attach method and the Deferred layout of child groups demo.

incrementalDraw

Determines how FoamTree decides whether to apply incremental visualization redraws. The following values are supported:

fast

FoamTree will try to redraw as few groups as possible when an incremental update is possible. With typical settings, this procedure will result in quick updates and no visual artifacts.

In case of non-typical settings, such as when groupSelectionOutlineWidth is larger than half the groupBorderWidth, you may occasionally see visual artifacts when using fast incremental updates. If this is the case, consider setting incrementalDraw to accurate.

accurate
FoamTree will apply a more conservative procedure when deciding whether an incremental draw can be performed. In particular, if groupSelectionOutlineWidth is larger than half the groupBorderWidth, FoamTree will always redraw all groups.
none
FoamTree will always redraw all groups, even if the settings permit incremental redraws.

wireframeToFinalFadeDelay

Once animation of the visualization completes, FoamTree will redraw the final image using more detail. This option determines the amount of time that should pass after the last animation frame before the high-quality image is shown.

wireframeToFinalFadeDuration

The duration of the fading animation used to switch between the wireframe and the high-quality visualization image.

finalToWireframeFadeDuration

The duration of the fading animation used to switch between the high-quality and the wireframe visualization image.

wireframeDrawMaxDuration

The desired maximum duration of drawing one frame of animation. Decreasing the value, possibly to 0, will make the animation more smooth at the cost of lower number of details. Increasing the value will increase the number of details, but may slow down the animation.

finalCompleteDrawMaxDuration

The desired maximum duration of a complete high-quality redraw of the visualization. Please see the Visualization rendering performance section for a detailed discussion.

finalIncrementalDrawMaxDuration

The desired maximum duration of an incremental high-quality redraw of the visualization. Please see the Visualization rendering performance section for a detailed discussion.

androidStockBrowserWorkaround

There is a bug in Android stock browsers, that affects the incremental updates of the visualization as well as cross-fading between the wireframe and final visualization images. Setting this option to true will enable a workaround for the bug. As a side effect, cross fading between the wireframe and final images will be disabled, regardless of the wireframeToFinalFadeDuration amd finalToWireframeFadeDuration options.

The default value of this option is true on all Android browsers and false on all other browsers.

viewport

A read-only option that returns the parameters of the current viewport. This option returns an object with the following properties:

x
The horizontal offset of the viewport.
y
The vertical offset of the viewport.
scale
The scale of the viewport. Values larger than 1.0 mean the viewport is magnified. A value of 1.0 means the viewport is in its neutral state. Values lower than 1.0 mean the viewport is demagnified, which may happen when some groups are exposed.

The following example renders the current viewport parameters inside each group's polygon.

foamtree.set({
  dataObject: { groups: randomGroups(10) },

  groupLabelDecorator: function (opts, params, vars) {
    var viewport = this.get("viewport");
    vars.labelText =
      "x\u00a0=\u00a0" + viewport.x.toFixed(2) + " " +
      "y\u00a0=\u00a0" + viewport.y.toFixed(2) + " " +
      "scale\u00a0=\u00a0" + viewport.scale.toFixed(2);
  }
});

The example below shows how to prevent panning of the visualization area if the viewport is not zoomed-in.

foamtree.set({
  dataObject: { groups: randomGroups(10) },

  onGroupDragStart: function (e) {
    if (this.get("viewport").scale <= 1.0) {
      return false;
    }
  }
});

Export

imageData

Returns the current state of the visualization as an image in the data URL format. Unlike most attributes, this one accepts an optional object which can specify the image format details. This object can contain the following keys:

format
format of the image data: image/png or image/jpeg
quality
if format is image/jpeg, specifies the desired quality of JPEG compression in the 0..1 range, where 1 means the highest quality and largest image data. Note that JPEG images are always opaque, even if the background color is specified as transparent. Use PNG images to handle background transparency.
pixelRatio
the pixel ratio to use when producing the export image. Use a value larger than 1, such as 2 to create a higher-resolution image.
backgroundColor
if specified, the background of the exported image will be filled in with the provided color. This option is especially useful when exporting images in the JPEG format.

The visualization image can be used by your application in many different ways. On certain browsers, it is possible to trigger a dialog allowing the user to save the image to the local disk. The image data can be also sent to a server that will do further processing (for example save it or send it via e-mail somewhere).

Heads up!

If the attribution logo was fetched from a different domain than the page that embeds the visualization, getting the visualization image will not be possible due to security constraints.

The following example logs the size of the visualization image (the data URL string, not actual bytes) in various formats to console.

foamtree.set({
  dataObject: largeDataSet,
  onRolloutComplete: function() {
    var opts = [
      { format: "image/png" },
      { format: "image/jpeg", quality: 0.2 },
      { format: "image/jpeg", quality: 0.4 },
      { format: "image/jpeg", quality: 0.6 },
      { format: "image/jpeg", quality: 0.8 },
      { format: "image/jpeg", quality: 1 }
    ];
    $(opts).each(function(i, format) {
      console.log(format, "Image takes: " +
                  (foamtree.get("imageData", format).length / 1024).toFixed(0) + "kB");
    });
  }
});

Interactions

selection

You can use this option to obtain or alter the current selection.

getter

Calling get("selection") returns an object with the currently selected groups. The object contains one property, groups, containing an array of references to the data objects corresponding to the selected groups.

foamtree.set("dataObject", { groups: [
  { label: "Group 1", selected: true },
  { label: "Group 2" },
  { label: "Group 3", selected: true },
  { label: "Group 4" }]});

console.log(foamtree.get("selection").groups);
setter

You can set the selection option to alter the current selection. The provided value must be a multiple group selector designating the groups to select or deselect.

  • If the selector is an individual selector or an array of individual selectors, the designated groups will get selected, the state of the other groups will remain unchanged.

    foamtree.set("selection", "1");
    var timeout = window.setTimeout(function() {
      foamtree.set("selection", "2");
    }, 2000);
  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    selected
    If true or not provided, the designated groups will get selected. If false, the designated groups will get deselected.
    keepPrevious
    If true or not provided, only the state of the designated groups will be altered. If false, the state of the designated groups will be altered, the other groups will get closed.

    To deselect all groups, you can use the selector with the all property:

    foamtree.set("selection", ["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.set("selection", { all: true, selected: false });
    }, 2000);

Note: when selection state is changed by setting this option, the event listeners will not be called.

open

You can use this option to obtain or alter the set of open groups.

getter

Calling get("open") returns an object with the currently open groups. The object contains one property, groups, containing an array of references to the data objects corresponding to the open groups.

foamtree.set("dataObject", { groups: [
  { label: "Group 1", groups: [ { label: "Group 1.1" }, {label: "Group 1.2" } ], open: true },
  { label: "Group 2" },
  { label: "Group 3", groups: [ { label: "Group 3.1" }, {label: "Group 3.2" } ], open: true },
  { label: "Group 4" }]});

console.log(foamtree.get("open").groups);
setter

You can set this option to alter the currently open groups. The provided value must be a multiple group selector designating the groups to open or close.

  • If the selector is an individual selector or an array of individual selectors, the designated groups will get open, the state of the other groups will remain unchanged.

    foamtree.set("open", "1");
    var timeout = window.setTimeout(function() {
      foamtree.set("open", "2");
    }, 2000);
  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    open
    If true or not provided, the designated groups will get open. If false, the designated groups will get closed.
    keepPrevious
    If true or not provided, only the state of the designated groups will be altered. If false, the state of the designated groups will be altered, the other groups will get closed.

    To close all groups, you can use the selector with the all property:

    foamtree.set("open", ["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.set("open", { all: true, open: false });
    }, 2000);

Note: when open state is changed by setting this option, the event listeners will not be called.

Tip: to get a notification of the completed open/close animation, you can use the open method instead.

exposure

You can use this option to obtain or alter the set of exposed groups.

getter

Calling get("exposure") returns an object with the currently exposed groups. The object contains one property, groups, containing an array of references to the data objects corresponding to the exposed groups.

foamtree.set("dataObject", { groups: [
  { label: "Group 1", exposed: true },
  { label: "Group 2" },
  { label: "Group 3", exposed: true },
  { label: "Group 4" }]});

console.log(foamtree.get("exposure").groups);
setter

You can set this option to alter the currently open groups. The provided value must be a multiple group selector designating the groups to expose or unexpose.

  • If the selector is an individual selector or an array of individual selectors, the designated groups will be exposed, all other groups will get unexposed.

    foamtree.set("exposure", "1");
    var timeout = window.setTimeout(function() {
      foamtree.set("exposure", "2");
    }, 2000);

    The following example shows how to unexpose all groups.

    foamtree.set("exposure", ["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.set("exposure", []);
    }, 2000);

    A similar effect could be achieved using the reset method.

  • If the selector is an object, two additional properties are supported, apart from the common groups and all properties:

    exposed
    If true or not provided, the designated groups will get exposed. If false, the designated groups will be unexposed.
    keepPrevious
    If true, only the exposure of the designated groups will be altered. If false or not provided, the exposure of the designated groups will be altered, the other groups will get unexposed.

    The following example uses the keepExpose property of the group selector to expose one more group in addition to the already exposed ones. Notice that if the parent group of the group to be exposed is closed (groups 3 and 4 in the example), the parents will be automatically opened as part of the exposure process.

    foamtree.set("exposure", ["1", "2"]);
    var timeout = window.setTimeout(function() {
      foamtree.set("exposure", { groups: ["3.1", "4.1"], keepPrevious: true });
    }, 2000);

Note: when exposure is changed by setting this option, the event listeners will not be called.

Note: there are some limitations when relaxation is visible.

Tip: to get a notification of the completed expose animation, you can use the expose method instead.

state

Returns information about the state of the group. The returned object contains the following properties:

selected
true if the group is currently selected
hovered
true if the group is currently hovered over
open
true if the group is currently open
openness
the progress of the opening/closing animation on the 0..1 scale
exposed
true if the group is currently exposed
exposure
the progress of the expose animation on the 0..1 scale
transitionProgress
the progress of the rollout/pullback animation on the 0..1 scale
revealed
true if the group is being drawn on the screen, also when the group is currently invisible due to zooming/panning; false when the group is not being drawn on the screen, due to, for example, too small size or because the relaxation process has not yet revealed it.
browseable
Since 3.2.0 This property can assume three states:
true
FoamTree has computed the layout for the direct children of this group and the group can be opened for browsing.
false

FoamTree has attempted to compute the layout for the direct children of this group, but the layout could not be computed for any of the following reasons: a. the diameter of the child groups would not exceed groupMinDiameter, b. the total limit of maxGroups would have been exceeded if the layouts got computed, c. JavaScript standard floating point precision is not enough to compute the layout, which can happen for very deeply nested groups.

When the group is not browseable, you may want to allow the user to reload the visualization passing the non-browseable group as the dataObject, and possibly allow the user to get back to the original top-level data.

undefined
FoamTree has not yet attempted to compute the layout for the direct children of this group. FoamTree will attempt to compute the layout when at most maxGroupLevelsDrawn above this groups are open.
visible
true if the group is visible in the current viewport
labelDrawn
true if the group's label is being drawn; false if the label is not being drawn due to, for example, to small area of the polygon.

To retrieve the information about a group, provide the individual group selector as the second parameter of the get method:

var timeout = window.setTimeout(function() {
  console.log(foamtree.get("state", "1"));
}, 500);

interactionHandler

Determines which use interaction capture mechanism FoamTree should be using:

builtin
the built-in mechanism, suitable mostly for desktop devices
hammerjs
use Hammer.js to capture the interactions, see the Touch devices section for details.
external
all interaction events will be triggered externally, please see the Externally triggered interaction section for details.

The default value of this option is builtin on desktop browsers and hammerjs on touch-enabled devices.

Debugging

logging

Enables logging of some debug information to console.

times

Execution times and statistics. The details of the returned object are not guaranteed to work between versions but it may turn useful for debugging performance problems.

window.setTimeout(function() {
  console.log("Execution time stats: ", foamtree.get("times"));
}, 500);

Visualization events

This section describes events that relate to the life cycle of the whole visualization.

onModelChanging

Called after the pullback animation of the previous data model has completed, but before the visualization has parsed the new data model provided in the dataObject option and before the new data has been rendered. The listeners will be called with one argument – the new data object the visualization is about to parse and show.

You can use this event to modify and enhance the data model. For example, you can add or remove groups and sub-groups at this stage, as in the following example:

foamtree.set({
  onModelChanging: function (data) {
    data.groups.forEach(function (group) {
      group.label = "Group with " + group.count + " child groups";
      group.weight = group.count;

      // Generate the child groups
      group.groups = [];
      for (var i = 0; i < group.count; i++) {
        group.groups.push({ label: "Group " + i });
      }
    });
  }
});

foamtree.set("dataObject", { groups: [
  { count: 8  },
  { count: 12 },
  { count: 5  },
  { count: 4  },
  { count: 7  }
]});

In the context of each callback, this points to the involved instance of CarrotSearchFoamTree.

onModelChanged

Called after the pullback animation of the previous data model has completed and the visualization has parsed the new data model provided in the dataObject option, but before the new data has been rendered. The listeners will be called with one argument – the new data object the visualization is about to show.

You can use this event to enhance the data model. For example, if the computation of the group's polygon color is expensive, you may want to perform the computation once in the onModelChanged listener, store it in the data object and then use it in the groupColorDecorator, as shown in the following example.

foamtree.set({
  onModelChanged: function (data) {
    data.groups.forEach(function (group) {
      group.color = "hsl(" + (-60 * (group.sentiment - 1)) + ", 100%, 50%)";
    });
  },
  groupColorDecorator: function (opts, props, vars) {
    vars.groupColor = props.group.color;
  }
});

foamtree.set("dataObject", { groups: [
  { label: "Positive", sentiment: 1 },
  { label: "Negative", sentiment: -1 },
  { label: "Neutral",  sentiment: 0 }
]});

Note: group hierarchy changes, such as adding or removing groups or sub-groups, will not be taken into account in the onModelChanged. You can make such changes in the onModelChanging listener.

In the context of each callback, this points to the involved instance of CarrotSearchFoamTree.

onRolloutStart

Called just before the visualization starts the animated rollout. If the application implements some sort of loading indicator, the indicator should be hidden once this event is fired.

Heads up!

This listener will not be called when an empty (null, undefined) dataObject is set. To get a notification in such cases, register a listener for the onModelChanging or the onModelChanged event.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onRolloutComplete

Called after the rollout animation has completed. Please note that this listener will not be called when an empty dataObject is set, see onRolloutStart for an alternative for such cases.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onRelaxationStep

Invoked once per layout relaxation step, but only when relaxationVisible is true. The signature of the listener function should be the following:

function (relaxationProgress, relaxationComplete, relaxationTimeout)

where:

relaxationProgress
the progress of the relaxation process on the 0..1 scale
relaxationComplete
When true, this is the last step of relaxation because the requested relaxationQualityThreshold has been achieved.
relaxationTimeout
When true, this is the last step of relaxation because the relaxationMaxDuration has been exceeded.

You can use this event to create a progress indicator for the relaxation, just as the relaxation progress utility does.

onRedraw

Called after the internal- or API-triggered redraw of the visualization. The listener will be called with one boolean parameter set to true if the redraw as an incremental one.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

Any code in this callback should be minimal and ultra fast to keep the transition animations smooth.

The following example uses the onRedraw listener to count incremental and complete redraws. The data object contains two special groups that hold the counters. The example also defines a simple groupColorDecorator that draws the complete redraws group in red and the incremental redraws group in yellow.

foamtree.set({
  onRedraw: function (incremental) {
    var group = this.get("dataObject").groups[incremental ? 0 : 1];
    group.redraws++;
    group.label = group.redraws.toString();
  },
  groupColorDecorator: function(opts, props, vars) {
    switch (props.group.type) {
      case "complete":    vars.groupColor = "hsl(0, 65%, 75%)"; break;
      case "incremental": vars.groupColor = "hsl(60, 65%, 75%)"; break;
      default:            vars.groupColor = "hsl(0, 0%, 85%)";
    }
  },
  rolloutDuration: 1000
});

foamtree.set("dataObject", { groups: [
  { label: "0", redraws: 0, weight: 2, type: "incremental" },
  { label: "0", redraws: 0, weight: 2, type: "complete" },
  { label: "" }, { label: "" },
  { label: "" }, { label: "" },
  { label: "" }, { label: "" },
  { label: "" }, { label: "" }
]});

onViewReset

Invoked after the view has been reset by a user interaction. See the reset method for more details.

Group state events

This section documents events triggered when the state of one or more groups is changed by the user's interaction.

onGroupSelectionChanging

Called during group selection changes. For each selected or deselected group, the listener will be invoked with one parameter containing the following properties:

group
Reference to the data object which is subject to selection state change.
selected
true if the group has just been selected, false otherwise

Note that one user action may result in many events of this type, for example deselection of currently selected groups and selection of another group. The global selection state is easier to track using onGroupSelectionChanged event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the selection via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupSelectionChanged

Called once after the selection has changed. The callback receives one parameter: an object containing the groups property with an array of references to the data objects representing the groups that are currently selected.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the selection via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

foamtree.set({
  onGroupSelectionChanged: function(info) {
    console.log(info);
  }
});

onGroupExposureChanging

Called during group exposure changes. For each exposed or unexposed group, the listener will be invoked with one parameter containing the following properties:

group
Reference to the data object which is subject to exposure state change.
exposed
true if the group has just been exposed, false otherwise
indirect
true if the exposure change is a side effect of some other user interaction, such as view reset.

Note that one user action may result in many events of this type, for example unexposure of currently exposed groups and exposure of another group. The global exposure state is easier to track using onGroupExposureChanged event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupExposureChanged

Called once after the exposure has changed. The callback receives one parameter: an object containing the following properties:

groups
an array of references to the data objects representing the groups that are currently exposed
indirect
true if the exposure change is a side effect of some other user interaction, such as view reset.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

foamtree.set({
  onGroupExposureChanged: function(info) {
    console.log(info);
  }
});

onGroupOpenOrCloseChanging

Called during group opening state changes. For each opened or closed group, the listener will be invoked with one parameter containing the following properties:

group
Reference to the data object which is subject to opening state change.
open
true if the group has just been opened, false otherwise
indirect
true if the state change is a side effect of some other user interaction, such as group exposure change or view reset.

Note that one user action may result in many events of this type, for example closing of currently opened groups and opening of another group. The global state is easier to track using onGroupOpenOrCloseChanged event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the state via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupOpenOrCloseChanged

Called once after some groups have been opened or closed. The callback receives one parameter: an object containing the following properties:

groups
an array of references to the data objects representing the groups that are currently open
indirect
true if the state change is a side effect of some other user interaction, such as group exposure change or view reset.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

foamtree.set({
  onGroupOpenOrCloseChanged: function(info) {
    console.log(info);
  }
});

Interaction events

This section documents events related to low-level interactions initiated by the user. Listeners registered for the those events will receive the event details object that they may use to prevent the default action associated with the specific event.

onGroupClick

Called after a mouse click (or tap gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

The following example shows how to make certain group unselectable.

foamtree.set({
  dataObject: { groups: [
    { label: "Selectable" },
    { label: "Selectable" },
    { label: "Selectable" },
    { label: "Unselectable", unselectable: true },
    { label: "Unselectable", unselectable: true }
  ]},
  onGroupClick: function (event) {
    if (event.group.unselectable) {
      event.preventDefault();
    }
  },
  groupColorDecorator: function (opts, props, vars) {
    vars.groupColor = props.group.unselectable ? "#aaa" : "#77ff00";
    vars.labelColor = props.group.unselectable ? "#888" : "auto";
  }
});

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupDoubleClick

Called after a double click (or double tap gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupHold

Called after a click-and-hold (or tap-and-hold gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupHover

Called after the mouse pointer enters the area covered by a group or leaves the visualization area completely. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action, which is highlighting of the hovered-on group.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupMouseMove

Called when the mouse pointer moves over the area of some group. The provided callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group and screen- and visualization-relative coordinates of the mouse pointer.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

You can use this event in combination with groupContentDecorator to make the custom-drawn elements of each group interactive. See the Interactive custom content demo for some example implementation.

onGroupMouseWheel

Called after the mouse wheel is rotated over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupMouseDown

Called after the mouse pointer is pressed. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupMouseUp

Called after the mouse pointer is released. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupDragStart

Called after the user starts dragging a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupDrag

Called when the user is dragging a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupDragEnd

Called when group dragging is complete. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupTransformStart

Called after the user starts touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupTransform

Called when the user is performing touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onGroupTransformEnd

Called when the user has completed touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

onKeyUp

Called when the user pressed a key. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, this points to the involved instance of CarrotSearchFoamTree.

Geometry utils

FoamTree exposes a number of geometry utilities useful when implementing groupContentDecorators. The utilities are grouped into a static object available from CarrotSearchFoamTree.geometry. Please see below for a detailed list of utility methods.

boundingBox(points)

Given an array of points, returns the bounding box of the points.

The method is available statically as:

CarrotSearchFoamTree.geometry.boundingBox(points)

where:

points

the array of point coordinates. Each element of the array must be an object containing the x and y properties representing the coordinates of the point.

<return value>

an object representing the bounding box. The object contains the x and y properties denoting the coordinates of the top-left corner of the rectangle, as well as the w and h properties denoting the width and height of the rectangle, respectively.

polygonCentroid(polygon)

Computes the centroid (center of mass) and area of the provided polygon.

The method is available statically as:

CarrotSearchFoamTree.geometry.polygonCentroid(polygon)

where:

polygon

the input polygon. The polygon must be specified as an array of objects, each object containing the x and y property representing the coordinates of the polygon's vertex.

The polygon must be non-self-intersecting, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the geometry option meet both criteria.

<return value>

an object containing the x and y properties denoting the coordinates of the centroid as well as the area property containing the area of the polygon in pixels.

rectangleInPolygon(polygon, cx, cy, wTH, scale, fx, fy)

Given a convex polygon, finds the largest inscribed rectangle of given aspect ratio wTH, centered around the provided center point (cx, cy). Additionally, the resulting rectangle can be scaled by the provided scale factor. Also, the alignment of the center point relative to the rectangle can be defined by the (fx, fy) point.

The method is available statically as:

CarrotSearchFoamTree.geometry.rectangleInPolygon(polygon, cx, cy, wTH, scale, fx, fy)

where:

polygon

the polygon to inscribe the rectangle into. The polygon must be specified as an array of objects, each object containing the x and y property representing the coordinates of the polygon's vertex.

The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the geometry option meet both criteria.

cx, cy

coordinates of the point inside the polygon around which the rectangle should be centered. For performance reasons, this method will not verify if the point lies inside the polygon. For points outside or on the boundary of the polygon, the result is not specified.

wTH

(optional) the aspect ratio of the polygon to inscribe, the ratio is assumed to be the polygons width divided by the polygon's height. If not provided, the ratio of 1.0 will be used, which will produce a square.

scale

(optional) the scale factor to apply to the resulting rectangle. The rectangle will be scaled relative to the alignment point determined by (fx, fy). If not provided, the scale of 1.0 will be used, which will produce the largest possible rectangle that fits inside the polygon and meets the center and alignment criteria.

fx, fy

(optional) defines what the the position of the center point (cx, cy) should be relative to the rectangle. The default value is (0.5, 0.5), which will position the rectangle in such a way that the center point is at the center of the rectangle. For (fx, fy) = (0.0, 0.0), the center point will overlap with the top-left corner of the rectangle. Similarly, when (fx, fy) = (1.0, 1.0), the center point will be at the bottom-right corner of the rectangle. Values larger than 1.0 or lower than 0.0 are also supported, in which case the center point will lie outside of the rectangle.

<return value>

an object representing the produced rectangle. The object contains the x and y properties denoting the coordinates of the top-left corner of the rectangle, as well as the w and h properties denoting the width and height of the rectangle, respectively.

The following example demonstrates the usage and results of this method.

foamtree.set({
  dataObject: { groups: [
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.0, scale: 0.7, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.0, scale: 0.5, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.0, fy: 0.0 },
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 1.0, fy: 1.0 },
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.2, fy: 0.7 },
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.2, fy: -0.5 },
    { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 1.5 },
    { ratio: 2.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
    { ratio: 0.5, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.2, scale: 1.0, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.5, scale: 1.0, fx: 0.5, fy: 0.5 },
    { ratio: 1.0, shift: 0.7, scale: 1.0, fx: 0.5, fy: 0.5 }
  ]},
  groupContentDecorator: function (opts, params, vars) {
    // Prepare some variables
    var centerX = params.polygonCenterX;
    var centerY = params.polygonCenterY;
    var polygon = params.polygon;
    var scale = params.group.scale;
    var shift = params.group.shift;
    var fx = params.group.fx, fy = params.group.fy;

    if (shift > 0) {
      // Shift towards one vertex to make sure the point stays within the polygon
      centerX = centerX * (1 - shift) + polygon[0].x * shift;
      centerY = centerY * (1 - shift) + polygon[0].y * shift;
    }

    // Compute the inscribed rectangle
    var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(
      polygon, centerX, centerY, params.group.ratio, scale, fx, fy);

    // Draw the rectangle
    var ctx = params.context;
    ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
    ctx.lineWidth = 2;
    ctx.strokeRect(box.x, box.y, box.w, box.h);

    // Draw the center point
    ctx.beginPath();
    ctx.arc(centerX, centerY, 5, 0, 2 * Math.PI);
    ctx.fill();

    // Draw the parameters too
    var fontSize = (params.boxHeight / 30);
    ctx.font = fontSize.toFixed(2) + "px Tahoma, sans-serif";
    ctx.textAlign = "center";
    ctx.fillText("(fx, fy) = (" + fx + ", " + fy + ")", centerX + 0.5 * fontSize, centerY - fontSize);
    ctx.fillText("scale = " + scale, centerX + 0.5 * fontSize, centerY - fontSize * 2);
  },
  titleBarDecorator: function (opts, params, vars) {
    // Display the details in the title bar on hover
    vars.titleBarShown = true;
    vars.titleBarText =
      "widthToHeight = " + params.group.ratio + "   " +
        "shiftFromCenter = " + params.group.shift + "   " +
        "margin = " + params.group.margin;
  }
});

circleInPolygon(polygon, cx, cy)

Given a convex polygon, finds the largest inscribed circle centered around the provided point.

The method is available statically as:

CarrotSearchFoamTree.geometry.circleInPolygon(polygon, cx, cy)

where:

polygon

the polygon to inscribe the circle into. The polygon must be specified as an array of objects, each object containing the x and y property representing the coordinates of the polygon's vertex.

The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the geometry option meet both criteria.

cx, cy

coordinates of the point inside the polygon around which the circle should be centered. For performance reasons, this method will not verify if the point lies inside the polygon. For points outside or on the boundary of the polygon, the result is not specified.

<return value>

the radius of the inscribed circle in pixels.

The following example demonstrates the usage and results of this method.

foamtree.set({
  dataObject: { groups: [
    { shift: 0.0, margin: 0.0 },
    { shift: 0.0, margin: 0.2 },
    { shift: 0.0, margin: 0.4 },
    { shift: 0.0, margin: 0.6 },
    { shift: 0.2, margin: 0.0 },
    { shift: 0.4, margin: 0.0 },
    { shift: 0.6, margin: 0.0 }
  ]},
  groupContentDecorator: function (opts, params, vars) {
    // Prepare some variables
    var centerX = params.polygonCenterX;
    var centerY = params.polygonCenterY;
    var polygon = params.polygon;
    var margin = params.group.margin;
    var shift = params.group.shift;

    if (shift > 0) {
      // Shift towards one vertex to make sure the point stays within the polygon
      centerX = centerX * (1 - shift) + polygon[0].x * shift;
      centerY = centerY * (1 - shift) + polygon[0].y * shift;
    }

    // Compute the inscribed circle
    var radius = CarrotSearchFoamTree.geometry.circleInPolygon(
      polygon, centerX, centerY) * (1 - margin);

    // Draw the circle
    params.context.lineWidth = 2;
    params.context.beginPath();
    params.context.arc(centerX, centerY, radius, 0, 2 * Math.PI);
    params.context.stroke();

    // Draw the center point
    params.context.beginPath();
    params.context.arc(centerX, centerY, 5, 0, 2 * Math.PI);
    params.context.fill();
  },
  titleBarDecorator: function (opts, params, vars) {
    // Display the details in the title bar on hover
    vars.titleBarShown = true;
    vars.titleBarText =
      "shiftFromCenter = " + params.group.shift + "   " +
        "margin = " + params.group.margin;
  }
});

stabPolygon(polygon, cx, cy, angle)

Splits the given convex polygon into two polygons along the line defined by the provided point and angle.

The method is available statically as:

CarrotSearchFoamTree.geometry.stabPolygon(polygon, cx, cy, angle)

where:

polygon

the polygon to split. The polygon must be specified as an array of objects, each object containing the x and y property representing the coordinates of the polygon's vertex.

The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the geometry option meet both criteria.

cx, cy

coordinates of the point that defines the splitting line.

angle

the angle defining the splitting line, in radians.

<return value>

an array of two polygons being the result of the split or undefined if the provided line does not intersect the provided polygon.

An example result is shown in the figure, where one of the resulting polygons (drawn in white dashed line) has 4 vertices and the other (drawn in black dashed line) has 5 vertices.

Please note the following properties of the returned polygons:

  • Each polygon is represented by an array of objects representing the polygon's vertices; each such object has the x and y properties defining the coordinates of the vertex.
  • Both returned polygons are convex with vertices enumerated in the clockwise order.
  • The returned polygons have one edge in common as shown by the black-white dashed in the figure. The two points defining the common edge in both polygons are always at indices 0 and 1 in the corresponding arrays, although not in the same order to preserve the clockwise enumeration order of the vertices.
  • The order of the two resulting polygons in the returned array is deterministic. For example, if angle = 0, the polygon at index 0 will be the upper one, polygon at index 1 will be the lower one. When the angle becomes 90 degrees, polygon at index 0 will be on the left, polygon at index 1 will be on the right.
  • If the provided line overlaps with any edge of the provided polygon or contains any vertex of the provided polygon, the returned polygons may be degenerate (e.g. have zero area and duplicated vertices).

The following example demonstrates the usage and results of this method.

foamtree.set({
  dataObject: {
    groups: (function () {
      var groups = [];
      for (var i = 0; i < 24; i++) {
        groups.push({ angle: Math.PI * i / 24 });
      }
      return groups;
    })()
  },
  relaxationInitializer: "squarified",
  groupBorderRadius: 0,

  groupContentDecorator: function (opts, params, vars) {
    // Split the polygon along its center
    var split = CarrotSearchFoamTree.geometry.stabPolygon(params.polygon,
      params.polygonCenterX, params.polygonCenterY, params.group.angle);

    // If splitting was successful, draw both polygons
    if (split) {
      var j;
      var ctx = params.context;
      ctx.setLineDash([8, 8]);

      ctx.fillStyle = "blue";
      ctx.beginPath();
      ctx.arc(params.polygonCenterX, params.polygonCenterY, 5, 0, Math.PI * 2);
      ctx.fill();

      // Draw the first polygon
      ctx.lineWidth = 3;
      ctx.strokeStyle = "white";
      ctx.beginPath();
      ctx.moveTo(split[0][0].x, split[0][0].y);
      for (j = 1; j < split[0].length; j++) {
        ctx.lineTo(split[0][j].x, split[0][j].y);
      }
      ctx.closePath();
      ctx.stroke();

      // Draw the second polygon. We set a line dash offset
      // and the use unusual iteration order to draw the common
      // edge of the two polygons in a coherent way (start and end
      // the line in at the same points). This will ensure that
      // the dashed elements don't overlap.
      ctx.lineDashOffset = 8;
      ctx.strokeStyle = "black";
      ctx.beginPath();
      ctx.moveTo(split[1][1].x, split[1][1].y);
      var len = split[1].length;
      for (var i = len - 1; i >= 1; i--) {
        var p = split[1][(i + 1) % len];
        ctx.lineTo(p.x, p.y);
      }
      ctx.closePath();
      ctx.stroke();
    }
  }
});

Drawing context

The groupContentDecorator exposes a drawing context buffer that must be used do draw the custom content of the polygon. While the provided context behaves like the standard CanvasRenderingContext2D, it also exposes a number of useful non-standard methods.

roundRect(x, y, w, h, r)

Prepares a path of a rectangle with rounded corners. The path can then be filled and/or stroked depending on the needs.

This method accepts the following parameters:

x, y

coordinates of the top-left corner of the rectangle.

w, h

width and height of the rectangle, in pixels.

r

radius of the corners, in pixels.

The following example demonstrates the this method.

foamtree.set({
  dataObject: { groups: randomGroups(30) },

  groupContentDecorator: function (opts, params, vars) {
    var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(
      params.polygon, params.polygonCenterX, params.polygonCenterY,
      4, 0.8);

    var ctx = params.context;
    ctx.roundRect(box.x, box.y, box.w, box.h, box.h * 0.1);
    ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
    ctx.fill();
    ctx.strokeStyle = "rgba(255, 255, 255, 0.7)";
    ctx.stroke();
  }
});

fillPolygonWithText(polygon, cx, cy, text, options)

Fills the provided polygon with text. If needed, this method will split the text into multiple lines and then record the appropriate fillText() calls to the context on which the method was called.

Please note that it may happen that the method is unable to fill the polygon with text given the provided set of options. Therefore, the callers must check the information object returned by this method to ensure the text was actually fit in the polygon.

This method accepts the following parameters:

polygon

the polygon to fill the text with. The polygon must be specified as an array of objects, each object containing the x and y property representing the coordinates of the polygon's vertex.

The polygon must be convex, vertex coordinates must be enumerated in the clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the geometry option meet both criteria.

cx, cy

the center point that determines the location of the text within the polygon.

text

the text to fill the polygon with. This method supports a number of special characters in the text.

options

Additional options for this method:

fontFamily
the font family to assume when filling the polygon with text, sans-serif by default.
fontWeight
Since 3.2.2 the font weight (e.g. bold) to assume when filling the polygon with text, normal by default.
fontStyle
Since 3.2.2 the font style (e.g. italic) to assume when filling the polygon with text, normal by default.
fontVariant
Since 3.2.2 the font variant (e.g. small-caps) to assume when filling the polygon with text, normal by default.
minFontSize
the minimum font size in pixels to use for filling the text, 0 by default.
maxFontSize
the maximum font size in pixels to use for filling the text, 72 by default.
lineHeight
the line height to assume when splitting the text into mulitple lines, 1.05 by default. Line heights smaller than 1.0 are currently not supported.
verticalAlign
the vertical align of the text relative to the center point (cx, cy), center by default. The following allignments are supported:
top
the top line of the text will be at the cy coordinate of the center point
bottom
the bottom line of the text will be at the cy coordinate of the center point
center
the text will be centered vertically around the center point

Please note that currently the horizontal align option is not available, the text will always be centered horizontally.

horizontalPadding
the horizontal padding to apply to each line of text. The unit of this option is the font size used to render the text. Default value: 1.0.
verticalPadding
the vertical padding to apply to the whole block of text. The unit of this option is the font size used to render the text. Default value: 0.5.
maxTotalTextHeight
The maximum total height of the text block as a fraction of the polygon's bounding box height, 0.95 by default.
allowEllipsis
if true and the complete text does not fit in the polygon, fill as much text as possible and append the ellipsis to replace the non-fitting part of the text, false by default.
allowForcedSplit
if true the text can be split at arbitrary points, such as inside words, during the filling process, false by default.
cache

if a non-null object is provided, this method will use it to cache some geometry information to speed up the text filling process. When another text filling request is made and the same cache object is presented, this method will try to reuse the cached information to speed up the layout process. If, however, the geometry of the polygon changes a lot, the cache will not be used and a complete filling process will be performed.

For caching to work properly, dedicated cache objects need to be provided for each logical polygon. For example, each group polygon will require a separate cache object. Moreover, to perform caching, the area option must also be provided.

Default value: undefined, meaning no caching will be performed.

area
the current area of the polygon for which the filling is performed. The area must only be provided if layout caching is desired.
cacheInvalidationThreshold
determines how much the area of the polygon must change in order to discard the cached layout and invoke the complete text fitting process, 0.05 by default.
invalidate
if true, the cache will be invalidated and full text filling process will be performed, regardless of whether the geometry of the polygon changed or not. If the label text or font has changed, you must set the invalidate option to true to ensure the text fitting is correctly performed.
<return value>

an object summarizing the text filling process:

fit
true when the text fit in the polygon. If this property is false, then no text drawing commands were issued and the returned summary object does not have any other properties.
lineCount
the number of lines into which the text was split.
fontSize
the font size, in pixels, in which the text was drawn.
box
the bounding box of the complete text block, an object with the x, y, w and h properties representing the top-left corner coordinates, width and height, respectively.
ellipsis
true if the ellipsis was applied at the end of the text.

The following example demonstrates the results of invoking this method with different options.

foamtree.set({
  dataObject: { groups: [
    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Some short text" },

    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Some italic text", style: "italic" },
    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Some bold text", weight: "bold" },
    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Some small-caps text", variant: "small-caps" },

    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Some text longer by a small bit" },

    { align: "center", minFontSize: 0, ellipsis: false,
      text: "A rather long text which should still fit the polygon pretty comfortably" },

    { align: "center", minFontSize: 40, ellipsis: false,
      text: "A rather long text that will most likely not fit the polygon with some " +
            "large minFontSize. A rather long text that will most likely not fit " +
            "the polygon with some minFontSize."},

    { align: "center", minFontSize: 40, ellipsis: true,
      text: "A rather long text that will most likely require an ellipsis to fit the " +
            "polygon at some large minFontSize. A rather long text that will most likely " +
            "require an ellipsis to fit the polygon at some large minFontSize." },

    { align: "center", minFontSize: 0, ellipsis: false,
      text: "Testing vertical align of 'center'" },

    { align: "top",    minFontSize: 0, ellipsis: false,
      text: "Testing vertical align of 'top'" },

    { align: "bottom", minFontSize: 0, ellipsis: false,
      text: "Testing vertical align of 'bottom'" }
  ]},

  groupContentDecorator: function (opts, params, vars) {
    var ctx = params.context;

    var group = params.group;
    var fillTextInfo = ctx.fillPolygonWithText(
      params.polygon,
      params.polygonCenterX, params.polygonCenterY,
      group.text, {
        fontWeight: group.weight || "normal",
        fontStyle: group.style || "normal",
        fontVariant: group.variant || "normal",
        minFontSize: group.minFontSize,
        allowEllipsis: group.ellipsis,
        verticalAlign: group.align
      });

    if (!fillTextInfo.fit) {
      // Draw a red cross instead
      var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(params.polygon,
        params.polygonCenterX, params.polygonCenterY, 1.0, 0.8);
      ctx.lineWidth = 3;
      ctx.strokeStyle = "red";
      ctx.moveTo(box.x, box.y);
      ctx.lineTo(box.x + box.w, box.y + box.h);
      ctx.moveTo(box.x + box.w, box.y);
      ctx.lineTo(box.x, box.y + box.h);
      ctx.stroke();
    }
  }
});

scratch()

Creates and returns a temporary drawing context buffer. Combined with the replay() method, the temporary contexts can be useful in the following scenarios:

  1. Drawing a rectangle beneath the text that was fitted in a polygon. The fillPolygonWithText() method issues the drawing commands immediately, but in order to draw a rectangle beneath the text, we need to know the bounding box of the text in advance. The solution is to invoke the text filling on the temporary context, draw the rectangle based on the text filling info returned by fillPolygonWithText() and finally replay the temporary text buffer buffer to draw the text on top of the rectangle. The following example demonstrates this approach.

      foamtree.set({
        dataObject: { groups: randomGroups(20) },
    
        groupContentDecorator: function (opts, params, vars) {
          var ctx = params.context;
          var group = params.group;
    
          var scratch = ctx.scratch();
          var fillTextInfo = ctx.fillPolygonWithText(
            params.polygon,
            params.polygonCenterX, params.polygonCenterY,
            "Group\u00a0" + (params.index + 1));
    
          if (fillTextInfo.fit) {
            // Draw the rectangle first
            var box = fillTextInfo.box;
            var padding = box.h * 0.15;
            ctx.lineWidth = 2;
            ctx.roundRect(box.x - padding, box.y - padding,
              box.w + 2 * padding, box.h + 2 * padding, padding);
            ctx.globalAlpha = 0.2;
            ctx.fill();
            ctx.globalAlpha = 0.6;
            ctx.stroke();
            scratch.replay(ctx);
          }
        }
      });
  2. Caching expensive layout computations. When groupContentDecoratorTriggering is onSurfaceDirty, the content decorator will be invoked each time the visualization needs to be redrawn. During most of those calls, the geometry of the groups' polygons will remain the same, so there may be no need recompute the layout of the custom content. One way to cache the layout in such cases would be to create one or more drawing context buffers per group, fill the buffers only when the shapeDirty property of the decorator is true and when shapeDirty is false, replay the previously buffered drawing commands. Please see the GitHub search example for this pattern applied in practice.

replay(targetContext)

Replays the content of the drawing context buffer to the targetContext. The target context can either be another context buffer of the actual native drawing context. For usage scenarios and examples, please see the scratch() method.

Version 3.5.0

released on 19.01.2021

The 3.5.0 exposes FoamTree as an NPM dependency for modern software building systems.

The 3.5.0 release is a drop-in replacement for the earlier 3.4.x releases.

New features

NPM dependency

Starting with version 3.5.0, in addition to the conventional downloaded package, FoamTree is also be available as an NPM dependency. Both the branded demo and the non-branded licensed build is available in the new format.

Improvements

Attribution group interaction
Previous versions would open the attribution URL immediately after clicking the attribution group. Since version 3.5.0 a ctrl+click is required to open the URL.

Version 3.4.10

released on 29.11.2019

The 3.4.10 makes it possible to customize the initial position of groups.

The 3.4.10 release is a drop-in replacement for the earlier 3.4.x releases.

New features

Custom initial group position
Version 3.4.10 makes it possible to customize the initial positions of specific groups. See the initialPosition property of the group data object for more details.

Bug fixes

JavaScript error on Android
and large zoom level
Previous versions would throw JavaScript errors and lock up when crossing a certain large zoom level on Android devices. This release fixes the problem.
Legacy Android browser
workaround disabled
A workaround for a bug in legacy Android stock browser is now disabled by default.

Version 3.4.9

released on 10.09.2019

The 3.4.9 fixes a number of minor issues.

The 3.4.9 release is a drop-in replacement for the earlier 3.4.x releases.

New features

Description groups in
hierarchical stacking
Version 3.4.9 makes it possible to allocate extra space for group descriptions also in hierarchically-stacked layouts. See the descriptionGroup option for more details and code examples.
Updating weights of
individual groups

As of version 3.4.9, the update method can take an optional parameter specifying the list of groups whose weights to update.

Bug fixes

groupLabelDrawn
impossible to reset

Previous releases would only let groupContentDecorator set the groupLabelDrawn variable to false. Setting the variable to true would not work. Version 3.4.9 fixes the issue.

Version 3.4.8

released on 04.07.2019

The 3.4.8 fixes an important performance issue affecting visualizations in flattened stacking mode, upgrade is strongly recommended. This version also documents how to combine lazy loading of groups with flattened stacking mode.

The 3.4.8 release is a drop-in replacement for the earlier 3.4.x releases.

Bug fixes

Full redraws in
flattened stacking mode

Previous versions would trigger a full visualization redraw when hovering over top-level groups in flattened stacking mode. This could be a significant performance issue on larger data sets. Version 3.4.8 fixes the issue and issues incremental redraws whenever possible.

Mouse wheel listener
declared as non-passive

As of version 3.4.8, FoamTree declares the internal mouse wheel event listener as non-passive to avoid browser warnings.

Lazy loading in flattened
stacking mode

Documentation of previous versions incorrectly stated that lazy loading was not supported in flattened stacking mode. In fact, version 3.4.5 exposed the attach() method that makes lazy loading possible with flattened stacking.

The lazy loading code example has been updated to work with both stacking modes.

Version 3.4.7

released on 23.05.2019

The 3.4.7 fixes a number of small software bugs related to flattened stacking mode and visualization resizing.

The 3.4.7 release is a drop-in replacement for the earlier 3.4.x releases.

Bug fixes

FoamTree may disappear
on resize

Previous versions are affected by a bug that can cause FoamTree to disappear when the container element is resized. The 3.4.7 release fixes the issue.

Hovering over exposed
flattened groups

When in flattened stacking mode, hovering over some exposed groups would highlight the non-exposed groups beneath the exposed ones. Version 3.4.7 correctly highlights the exposed group on top.

Double-click detection

Previous versions may fail to detect double-double click event in some areas of the visualization when the display is stretched, that is FoamTree container changed its size, but resize() wasn't called. Version 3.4.7 fixes the issue.

Label rendering in
flattened mode

When stacking is set to flattened and group hierarchy is deeper than maxGroupLevelsDrawn, FoamTree might not render labels of the deepest visible groups. Version 3.4.7 fixes the issue.

Version 3.4.6

released on 28.12.2018

The 3.4.6 fixes a number of small software bugs and documentation omissions.

The 3.4.6 release is a drop-in replacement for the earlier 3.4.x releases.

Bug fixes

JavaScript errors
when calling dispose()

Previous versions might throw JavaScript errors when dispose() was called with pullback animations running at the same time. Version 3.4.6 fixes the issue.

Legacy mouse wheel events

Previous versions would use the non-standard mousewheel event which would prevent Chrome from reacting on the standard wheel events in other parts of the page. Version 3.4.6 fixes the issue by switching to listening to the standard wheel event.

Documentation extensions

description property

Documentation of description property has been added to the hierarchy option documentation.

Version 3.4.5

released on 26.07.2017

The 3.4.5 adds a number of minor improvements, bug fixes and documentation extensions.

The 3.4.5 release is a drop-in replacement for the earlier 3.4.x releases.

Improvements

Deferred layout
of child groups

You can use the maxGroupLevelsAttached option along with the attach method to defer the layout of lower hierarchy levels until the layout of the top level is computed and shown to the user. This can improve the responsiveness of the visualization during the initial data loading. See the Deferred layout demo for a complete code example.

Performance tuning
tips added

The layout performance tuning section has been extended with tips about deferred layout computation and replacing expose with zoom in.

Bug fixes

Lazy initialization
of child groups
on group open

With lazy initialization of child groups enabled, FoamTree would not properly initialize child groups of a parent group whose opening would cause the maxGroupLevelsDrawn threshold to be met. The 3.4.5 release fixes the issue.

Version 3.4.4

released on 26.09.2016

The 3.4.4 adds a number of minor improvements, bug fixes and documentation extensions.

The 3.4.4 release is a drop-in replacement for the earlier 3.4.x releases.

Improvements

onGroupMouseMove event
and absolute coordinates

The 3.4.4 release introduces the onGroupMouseMove event that is continuously triggered when the user's mouse pointer hovers over some group. Additionally, the event details object will now contain the xAbsolute and yAbsolute properties that are visualization relative and independent of zoom, pan and expose transformations.

You can use the new event and visualization-relative coordinates to make the custom group content interactive.

FoamTree: interactive custom content demo
Container-relative
coordinate retrieval

You can use the containerCoordinates option to retrieve HTML container-relative coordinates of a specific point inside FoamTree visualization. You can use this option to implement, for example, sticky tooltips that always point at a specific point in the visualization, regardless of zoom, pan or expose transformations.

labelFontSize geometry()
property documented

Previous releases did not document the labelFontSize property returned by the geometry() method. This release adds the missing documentation.

Bug fixes

Initial selection
outline not drawn

Previous versions would not draw outline for initially selected and open groups if rolloutDuration is was set to zero. This release fixes the issue.

Needless layout
recomputation

Previous versions would needlessly recompute group layout in response to changing certain options, which might adversely impact performance. The 3.4.4 release fixes the issue.

Version 3.4.3

released on 11.07.2016

The 3.4.3 provides a number of minor bug fixes and improvements.

The 3.4.3 release is a drop-in replacement for the earlier 3.4.x releases.

Improvements

Needless recomputation
of rectangular layouts

Earlier 3.4.x releases would needlessly recompute the rectangular group layouts, which might impact the performance when visualizing very large data sets. The 3.4.3 release fixes the issue.

Incorrect title bar position

Earlier 3.4.x releases might incorrectly position the title bar and obscure the group being hovered over. This would only happen for rectangular layouts with stacking set to flattened and descriptionGroupPosition equal to bottom. The 3.4.3 release fixes title bar positioning in this case.

Label-too-small dots
also drawn for
custom content

As of the 3.4.3 release, the label-too-small dots will also be drawn when the groupContentDecorator is implemented. Drawing of label-too-small dots is controlled using the groupLabelMinFontSize option.

Documentation for label
bounding box properties

Documentation of the geometry() method lacked the description of properties related to the bounding box of the group's label.

Version 3.4.2

released on 13.11.2015

The 3.4.2 fixes a bug that prevented FoamTree demos from running when opened using the file:// protocol.

The 3.4.2 release is a drop-in replacement for the earlier 3.4.x releases.

Bug fixes

file:// protocol in demos

FoamTree 3.4.1 demos would fail when opened in the browser using file:// protocol. Version 3.4.2 fixes this issue.

Version 3.4.1

released on 09.11.2015

The 3.4.1 provides a number of small bug fixes and improvements.

The 3.4.1 release is a drop-in replacement for the 3.4.0 release.

Improvements

Consecutive new line chars
in group labels respected

The 3.4.1 respects multiple consecutive new line characters (\n) in labels, so that you can create empty lines in labels.

Small labels in
high-resolution export

When exporting visualization imageData with a high pixel ratio, FoamTree 3.4.1 will draw small-font labels rather than the "..." ellipsis.

New demos

Interactive label editing

Release 3.4.1 adds a demo with a simple utility for interactive label editing.

Bug fixes

Certain groups may be
rendered transparent

Releases prior to 3.4.1 may render certain groups transparent if relaxationVisible is set to true and groupColorDecorator is used. The 3.4.1 release fixes this issue.

Broken selection outline
on open & selected groups

The 3.4.0 release may render broken selection outline when the selected group is open and its children are hovered over. The 3.4.1 release fixes this issue.

Container element's
position CSS property
overwriten

If FoamTree container element was positioned (had the CSS position property different from static), the position property would get reset after calling resize. The 3.4.1 release fixes this issue.

Version 3.4.0

released on 11.09.2015

The 3.4.0 release introduces the "flattened" view of the group hierarchy, a number of enhancements to custom group content and label drawing, performance improvements, bug fixes and new mini-app demos.

The 3.4.0 release is a drop-in replacement for the 3.3.x releases.

New features

"Flattened" hierarchy view
As of version 3.4.0 FoamTree can present the hierarchy in an alternative "flattened" view, shown on the left of the screen shot below. As opposed to the default hierarchical view, on the right in the screen shot, the "flattened" view shows all levels of the hierarchy at once. Flattened view of a Voronoi treemap in FoamTree For more details, please see the stacking option documentation section and the Unit test coverage, FT500 explorer and SCADA dashboard demos.

New demos

FT500 explorer
Financial Times 500 companies in FoamTree treemap

The FT500 visualizes the ranking of world's 500 largest companies according to Financial Times. The demo relies the newly introduced flattened view view of the group hierarchy.

Photo explorer
Flickr photo viewer based on the FoamTree treemap visualization library

The Photo explorer demo visualizes Flickr's top daily photos. It uses the newly introduced polygonContext property of groupContentDecorator to fill FoamTree polygons with bitmap images.

SCADA sensor dashboard
SCADA sensor readouts dashboard implemented using the FoamTree treemap visualization library

The SCADA dashboard demo uses to FoamTree to implement a dashboard of readouts from SCADA system sensors. The demo relies the newly introduced flattened view of the group hierarchy.

Fancy background
generator
SCADA sensor readouts dashboard implemented using the FoamTree treemap visualization library

Not very useful on its own, the Background demo shows how to use FoamTree to generate procedural and animated backgrounds for various kinds of content.

Context menu demo

The context menu demo shows how to implement group-specific context menus in FoamTree.

CodePen resources
We have just started our CodePen profile, which will contain some FoamTree demos to fork and experiment with as well as some short FoamTree API recipes that we create outside of the main FoamTree release cycle.

Improvements

Line breaks in labels
You can now force line breaks in group labels, see the list of special characters supported in labels.
More control in
custom content drawing
As of version 3.4.0, you can take complete control of how the group's polygon is drawn using the polygonContext property of groupContentDecorator. See the Photo explorer and SCADA dashboard demos for real-world applications of this property.
Getting group's neighbors
You can now retrieve the neighbors of a group as part of the geometry information; the Background demo shows an example usage.
Attribution color theme
You can now use the attributionTheme option to draw the attribution group in a light or dark color, works also in the demo binaries.
Better incremental drawing
The 3.4.0 release makes incremental drawing more intelligent and slightly faster. You can further tune it using the incrementalDraw option.
Nicer rounded corners
The 3.4.0 release improves the rendering of rounded corners at lower levels of deep hierarchies.

Bug fixes

Custom drawing context

The custom drawing context did not support reading of properties, such as lineWidth. Additionally, the custom drawing context would not correctly handle the drawImage method in high zoom levels.

Version 3.4.0 fixes both issues.

Title bar when shown
maxLabelSizeForTitleBar=0
Previous versions would show the title bar even when the maxLabelSizeForTitleBar was set to 0. Version 3.4.0 fixes the issue and does not show the title bar in such a setting.
Incorrect auto label color
Occasionally, FoamTree would fail to update the label color in response to the change in the group color, which may have resulted in low label-to-polygon contrast. The 3.4.0 release fixes the issue.
Infinite loop for zero
groupLabelVerticalPadding
Previous releases might enter an infinite loop when the groupLabelVerticalPadding option was set to 0. The 3.4.0 release fixes the issue.
initializer alias for
relaxationInitializer
does not work

After renaming the initializer option to relaxationInitializer, the 3.3.x and 3.4.x releases keep initializer as an alias to relaxationInitializer. Versions 3.3.0, 3.3.1, and 3.3.2, however, did not respect the alias. The 3.4.0 release fixes this issue.

Version 3.3.2

released on 03.04.2015

The 3.3.2 release fixes a number of minor issues found in version 3.3.1 and adds the 1.0.x legacy version emulation demo.

The 3.3.2 release is a drop-in replacement for the earlier 3.3.x releases.

Improvements

Incremental drawing
improvements

The 3.3.2 release replaces needless full visualization redraws with incremental ones. Also, the 3.3.2 release fixes possible drawing artifacts when groupSelectionOutlineWidth is larger than groupBorderWidth.

Bug fixes

JavaScript error when
visualization container
is extremely small

For extremely small visualization containers (below 60x60 pixels in size), FoamTree may throw a JavaScript error when the user hovers the mouse pointer over some groups. The 3.3.2 release fixes this issue.

Child groups may
disappear on resize

In versions prior to 3.3.2 child groups of small parent groups may disappear on visualization area resize. The 3.3.2 release fixes this issue.

Ellipsis may appear
for blank labels

Versions prior to 3.3.2 would attempt to draw a label even if it is an empty string. As a result, ellipsis might appear in some of the smaller groups with empty labels. The 3.3.2 release fixes this issue.

New demos

FoamTree 1.0.x emulation

Release 3.3.2 adds demo with emulation of the Flash-based 1.0.x line of FoamTree look and feel.

Version 3.3.1

released on 19.03.2015

The 3.3.1 release fixes a number of minor issues found in version 3.3.0.

The 3.3.1 release is a drop-in replacement for the 3.3.0 release.

Improvements

attributionWeight

Version 3.3.1 introduces the attributionWeight option that you can use to influence the size of the attribution group relative to other groups.

attributionLogoScale

Version 3.3.1 introduces the attributionLogoScale option that you can use to customize the scale at which FoamTree will draw the custom attribution logo.

attributionLogo accepts
the Image object

Since version 3.3.1, the attributionLogo option also accepts the Image instances. With this enhancement, you can render an SVG attribution logo that will look well regardless of the zoom level.

attribution flag
passed to decorators

As of version 3.3.1, the decorator functions, such as the groupColorDecorator, will receive an extra flag indicating if the currently processed group is an attribution group. Please see the hierarchy documentation for details.

Bug fixes

attributionPosition
not respected for
rectangular layouts

Version 3.3.0 did not respect the attributionPosition option when layout was set to ordered or squarified. Version 3.3.1 fixes this issue. Please note, however, that the position of the attribution site in rectangular layouts is limited to the top-left or bottom-right corner. Please see the attributionPosition documentation for more details.

Version 3.3.0

released on 16.02.2015

The 3.3.0 release introduces rectangular group layouts as well as a number of small improvements and bug fixes.

The 3.3.0 release is a drop-in replacement for earlier 3.2.x releases.

New features

Rectangular tree maps
As of version 3.3.0 FoamTree can generate the traditional rectangular tree maps in addition to the default polygonal layouts. Traditional rectangular treemap in FoamTree For more details, please see the layout option documentation section and the source code of the above demo.
Laying out groups
in left-to-right order

Version 3.3.0 comes with support for laying out groups in the left-to-right fashion according to their original order in the data object. Such ordered layout is possible with both the polygonal and rectangular treemaps.

To generate the ordered polygonal layout, set the relaxationInitializer to ordered; to generate the ordered rectangular layout, set layout to ordered. In both cases, set layoutByWeightOrder to false. Please see the ordered layouts demo source code for a complete example.

Bug fixes

indexByWeight property
uninitialized for
lower level groups

Versions 3.2.x leave the indexByWeight property, available for decorator functions and from the hierarchy option, uninitialized for non-root level groups. Version 3.3.0 fixes this issue.

API changes

initializer renamed to
relaxationInitializer

As of version 3.3.3, the initializer option would only be applicable when layout is set to relaxed. To emphasize this dependency, the initializer option has been renamed to relaxationInitializer. With this change, all options that apply only when layout is relaxed have the relaxation* prefix.

The 3.3.x and 3.4.x versions will keep initializer as an alias for relaxationInitializer. In 3.5.0, the initializer alias will be removed.

relaxationInitializer
value changes
To better accommodate the multiple initializer algorithms, some values of the relaxationInitializer option have been renamed as follows:
Old value New value
treemap becomes squarified
order becomes ordered

The 3.3.x and 3.4.x versions will keep the previous values as aliases. In 3.5.0 the aliases will be removed.

Version 3.2.3

released on 12.11.2014

The 3.2.2 release fixes a number of minor issues found in the earlier versions.

The 3.2.3 release is a drop-in replacement for earlier 3.2.x releases.

Improvements

Getting current zoom scale
As of version 3.2.3, you can get the viewport property to access the current zoom scale and offsets. This information may be useful when drawing custom content on a separate canvas on top of FoamTree.

Bug fixes

Preventing default action
for onGroupDragStart
should prevent panning

Earlier versions required preventing the default action of all onGroupDragStart, onGroupDrag and onGroupDragEnd to reliably disable panning of the visualization area. As of version 3.2.3, it is enough to prevent the default action of onGroupDragStart to achieve the same effect. Please see the viewport option for a practical example.

Incorrect gesture in the
interaction hints utility

The interaction guide utility incorrectly contained the right double-click as the gesture to close the group. This release corrects the name of the gesture to be right click-and-hold.

JavaScript errors
on unsupported browsers

Previous versions of FoamTree, when loaded on unsupported browsers, such as Internet Explorer 7, would throw JavaScript errors before the the calling code had a chance to inspect the supported property. Version 3.2.3 fixes this issue.

Version 3.2.2

released on 10.11.2014

The 3.2.2 release fixes a number of minor issues found in the earlier versions and adds the possibility to control the style, weight and variant of labels.

The 3.2.2 release is a drop-in replacement for earlier 3.2.x releases.

Improvements

Font style, weight and
variant customization
As of version 3.2.2, you can set the style, weight and variant of the font used to draw group labels, title bar and generic text. For details, see: groupLabelFontStyle, groupLabelFontWeight, groupLabelFontVariant, titleBarFontStyle, titleBarFontWeight, titleBarFontVariant and context.fillPolygonWithText.
HTML tooltips example
Version 3.2.2 comes with an example of displaying rich HTML-based tooltips for each group.
More precise
group hover detection
Version 3.2.2 takes into account the groupBorderWidth when reporting group hover change in the onGroupHover event. As a result, if the user pointer moves to the gap between groups, FoamTree will trigger onGroupHover event with an undefined group to indicate that no group is being hovered over.

Bug fixes

JavaScript errors when
showZeroWeightGroups
= false

Versions prior to 3.2.2 may throw JavaScript errors when some groups have zero weight and showZeroWeightGroups is set to false.

Version 3.2.2 fixes this issue.

Version 3.2.1

released on 09.09.2014

The 3.2.1 release fixes a number of minor issues found in the earlier versions and adds the possibility to control the position of the attribution group.

The 3.2.1 release is a drop-in replacement for earlier 3.2.x releases.

Improvements

Setting position of the
attribution group
As of version 3.2.1, you can set the desired position of the attribution group using the attributionPosition and attributionDistanceFromCenter attributes.
Lazy loading of data
Example code of lazy-loading of visualization data has been added.

Bug fixes

Touch-enabled desktop
devices supported

Versions prior to 3.2.1 would disable mouse- and keyboard-based interactions when support for touch input was detected. This would make it impossible to interact with FoamTree using keyboard and mouse on a regular desktop computer with a touch-enabled monitor attached.

Version 3.2.1 fixes this issue.

Correctly closing
unexposed groups

Versions prior to 3.2.1 would close an unexposed group only when he user right-double-clicked some child group of the exposed group. If the user right-double-clicked outside of the exposed group, the group would get unexposed, but it would remain open.

As of version 3.2.1, if a group was opened as part the expose interaction, it will get closed when the group gets unexposed, regardless of the gesture that triggered the unexposure.

groupContentDecorator
correctly set after
FoamTree is initialized
Versions 3.1.0 and 3.2.0 would ignore the groupContentDecorator if set after FoamTree was initialized. The 3.2.1 release fixes this issue.
JavaScript error in
groupContentDecorator
at high zoom levels
Version 3.2.0 may throw JavaScript errors inside groupContentDecorator code when drawing content at very high zoom levels. Version 3.2.1 fixes the issue.

Version 3.2.0

released on 08.07.2014

The 3.2.0 release makes it possible to browse very large hierarchies consisting of 100k+ groups at 100+ levels. It also introduces a number of minor related improvements and bug fixes.

The 3.2.0 release is a drop-in replacement for earlier 3.1.x releases in terms of API compatibility. For a list of functional changes, please see below.

New features

Support for browsing
very large data sets
As of version 3.2.0, FoamTree switches from eagerly precomputing the layout for all group levels to lazily initializing the layout when required. As a result of this change, you can now use FoamTree to browse hierarchies consisting of many thousands of nodes without noticeable performance degradation. FoamTree visualizing the Tree of Life For more details, please see the Visualizing large data sets section and the source code of the above demo.

Improvements

Programmatic zooming
You can now use the zoom method to zoom to the specified group.
Performance improvements

Version 3.2.0 significantly improves performance when rolloutDuration is 0. This setting is particularly useful when visualizing large numbers of groups on one level.

Also, you can use the maxGroupLevelsDrawn and maxGroupLabelLevelsDrawn options to improve the rendering performance at the cost of lowering the amount of detail.

Functional changes

Not all levels
of groups drawn

As of version 3.2.0, FoamTree draws only maxGroupLevelsDrawn of closed group levels. In some cases, such as lowered parentFillOpacity, you may want to increase the maxGroupLevelsDrawn to bring more group levels into view.

Version 3.1.0

released on 03.06.2014

The 3.1.0 release adds two major new features: drawing custom content in FoamTree polygons and the ability to dynamically update the weights of existing groups. Additionally, version 3.1.0 comes with a number of new demos, minor improvements and bug fixes.

The 3.1.0 release is a drop-in replacement for earlier 3.0.x releases.

New features

Drawing custom content
in FoamTree polygons
Since version 3.1.0, the groupContentDecorator option makes it possible to draw arbitrary content in each FoamTree polygon. Please see the examples in the documentation as well as the SVG images and GitHub search for example decorations that can be drawn. FoamTree GitHub search demo
Dynamic weight updates
It is now possible to dynamically update weights of the visualized groups, FoamTree will perform an animated transition between the old and new weight values. See the update method for a complete example.
Data generator
in settings panel
The settings panel now comes with a data generator that you can use to test your settings with data sets of different characteristics. FoamTree data generator dialog
New demos
Version 3.1.0 come with four new demos: GitHub search, CSV to FoamTree, Code coverage visualization and Images in polygons. All of the demos make advanced use of the new and existing FoamTree APIs. New demos in FoamTree 3.1.0

Improvements

Per-group layout initializers
You can now specify different layout initializers for each parent group separately.
Redraw hints
You can now suggest which groups need redrawing when calling the redraw method.
Zoom scale exposed
in titleBarDecorator
Current zoom scale is now provided in the titleBarDecorator, which may be useful to determine the title bar visibility when the content of the polygon is customized using the groupContentDecorator.

Bug fixes

Fixing a memory leak when
groupGrowingDuration > 0

When groupGrowingDuration was larger than 0, FoamTree would prevent the objects provided in the dataObject option from being garbage collected even after some new data was subsequently set for rendering. Please note that the default value of groupGrowingDuration is 0, so this issue does not affect set ups running with the default value of that option.

The 3.1.0 release fixes the memory leak.

Undocumented options

Since version 3.0.0, the imageData offered the backgroundColor option that may come especially useful when exporting JPEG images. The option was undocumented in previous versions.

Also, the order relaxationInitializer option was previously undocumented.

API changes

groupLabelDottingThreshold
removed

Version 3.1.0 removes the groupLabelDottingThreshold option. FoamTree will now always attempt to fit the group's label into its polygon and will draw the ellipsis instead of the label if the label's font size (taking the current zoom factor into account) is smaller than groupLabelMinFontSize.

Version 3.0.3

released on 10.04.2014

The 3.0.3 release fixes a number of rendering issues found in version 3.0.2.

The 3.0.3 release is a drop-in replacement for earlier 3.0.x releases.

Improvements

Better rendering
of small-font labels
Version 3.0.3 improves rendering of small-font labels of deeply-nested groups in Chrome, Safari and Firefox. The image below shows small-font label rendered in Chrome by FoamTree 3.0.2 (left) and FoamTree 3.0.3 (right). Improved small-font label rendering in FoamTree 3.0.3
Allowing default browser
actions on selected events
Version 3.0.3 makes it possible to allow default browser actions on selected events, such as mouse wheel movement. To allow the default browser event action, call the allowOriginalEventDefault() method on the appropriate interaction event details object. The scrollable content example shows how to use this technique to allow page content scrolling when mouse wheel is moved over FoamTree element.

Bug fixes

Incorrect handling
of numeric group
identifier 0
Fixes a problem with groups identified with an integer 0, which could not be selected and were ignored in methods accepting a list of identifiers.

Version 3.0.2

released on 14.02.2014

The 3.0.2 release fixes a number of rendering issues found in version 3.0.1 and provides example code that emulates the look and feel of FoamTree 2.0.x.

The 3.0.1 release is a drop-in replacement for version 3.0.1 and 3.0.0.

Improvements

FoamTree 2.0.x emulation
Version 3.0.2 adds a code example that emulates the look and feel of FoamTree 2.0.x. Please note that the example code requires FoamTree 3.0.2 or later to run (FOAMTREE-201).
FOAMTREE-203
As of version 3.0.2, the geometry option exposes the coordinates of the top-left corner of the group polygon's bounding box.
FOAMTREE-209
Version 3.0.2 reintroduces the indexByWeight group property known from FoamTree 2.0.x. The indexByWeight property is available in the groupColorDecorator callback and also directly through the hierarchy read-only option.

Bug fixes

Certain options ignored
Versions 3.0.0 and 3.0.1 would ignore certain options when provided at CarrotSearchFoamTree instantiation. When the same options were changed using the set method, the changes would not be ignored. The following options were affected by this bug: groupLabelFontFamily, groupLabelLineHeight, groupLabelHorizontalPadding, groupLabelVerticalPadding, groupLabelMaxTotalHeight, groupLabelMaxFontSize. FoamTree 3.0.2 fixes the issue (FOAMTREE-211).
Incorrect polygon clipping
Versions 3.0.0 and 3.0.1 would occasionally incorrectly clip the child groups' polygons, which may have resulted in distorted rendering and "flickering" during animated relaxation. Version 3.0.2 fixes the issue and also slightly improves the performance of polygon rendering (FOAMTREE-207, FOAMTREE-210).
All-black child groups
If a group is the only direct child group of some parent, versions 3.0.0 and 3.0.1 would render the child group in black, regardless of the lightness and color of the parent group. Version 3.0.2 fixes the issue (FOAMTREE-202).
Non-string group IDs
Versions prior to 3.0.2, did not resolve non-string group identifiers used in individual and multiple group selectors. Version 3.0.2 fixes this issue (FOAMTREE-206).

Version 3.0.1

released on 09.01.2014

The 3.0.1 release fixes a number of minor issues found in version 3.0.0.

The 3.0.1 release is a drop-in replacement for version 3.0.0.

Improvements

FOAMTREE-199
Version 3.0.1 improves rendering performance estimation on Webkit-based browsers for increased animation smoothness.
FOAMTREE-200
Version 3.0.1 improves the layout of demo pages viewed on mobile devices.

Bug fixes

FOAMTREE-197
Version 3.0.0 incorrectly computed touch event coordinates when Hammer.js was used to detect touch events. Version 3.0.1 fixes this issue and additionally improves the performance of event detection on both desktop and mobile devices.
FOAMTREE-198
Version 3.0.0 would throw a JavaScript error on browsers that support local storage in general but deny the use of it to the specific page in which FoamTree is embedded. Version 3.0.1 fixes this issue.

Version 3.0.0

released on 07.01.2014

FoamTree 3.0.0 is a major new release with the layout and display engine rebuilt from ground up and a number of API improvements.

New features

New layout engine
FoamTree 3.0.0 comes with a completely new layout and display engine. The new engine allows the same data to be displayed in many arrangements (such as with large groups in the center, small groups in the center or large groups in the corner, like in FoamTree 2.0.x) while preserving the group weight to polygon area relationship.
Zooming & panning
The new display engine allows you to zoom into specific groups and pan around the visualization. On touch devices, the natural scaling and panning gestures are supported.
Exposure
Double clicking on a group, zooms to the group, visually highlights it and opens for browsing the child groups.
New label fitting
The new label fitting algorithm is much faster and better uses the available polygon area. The new algorithm also supports special characters, such as non-breakable space, zero-width space and soft-hyphens.
Polygon decorations
New polygon decorations are now available, including stroke and drop shadow.
New effects
Using detailed settings, you can now create your own rollout and pullback effects.
Utility scripts
FoamTree now comes with a number of utility scripts which you can use to add data loading progress indicator or a simple interaction guide dialog.
API improvements
Version 3.0.0 introduces a number of API improvements, such as registering multiple listener functions for the same event or promises.

Migration

For typical use cases of the FoamTree API, version 3.0.0 should be a drop-in replacement for FoamTree 2.0.x. The most common adjustment, if any, would be updating your code not to use the options deprecated in version 2.0.x that have been removed in version 3.0.0. To quickly identify deprecated options in your code, run FoamTree with debugging assertions right after the update. You may also consider using some of the API improvements added in version 3.0.0.

If you have any problems porting your code to version 3.0.0, feel free to contact us for help.

Heads up!

While FoamTree 3.0.x is a drop-in replacement for the 2.0.x line in terms of the JavaScript API compatibility, it introduced a number of option defaults changes to refresh the visual appearance and improve interaction.

If you'd like to revert to the look and feel known from FoamTree 2.0.x, you can do so by tweaking a number of options as this example code shows. The example is available in FoamTree 3.0.2 and later.

API changes

Noteworthy improvements

multiple event listeners
You can now register multiple event listeners for the same type of event. Furthermore, you can now use the on and off methods to add and remove event listeners without affecting the other registered listeners.
promises
Groups can now be open and exposed using the open and expose methods. The methods return promises that get resolved after the associated animation completes.
group properties
You can now query various properties related to the group's state by reading the state, hierarchy and geometry options.
new group selectors
There are more ways to designate a group or groups to be selected, open or exposed. In particular, the data object representing the group can be used as a selector now.
preventing default actions
You can now prevent the default interactions by calling the preventDefault() method of the low-level event object in the appropriate low-level event, such as onGroupClick.

Deprecations

onGroupSelectionChanged
The array of selected groups previously available in the selectedGroups property of the onGroupSelectionChanged event details is now available as the groups property.
groupLabelDecorator
FoamTree 2.0.x would expose the labelColor variable both in groupLabelDecorator and groupColorDecorator. As of version 3.0.0, labelColor is only available in groupColorDecorator.

Renamed options

A number of options have been renamed to form consistent option-grouping prefixes or to follow conventions established by other option names.

labelDarkColor
renamed to groupLabelDarkColor
labelLightColor
renamed to groupLabelLightColor
labelColorThreshold
renamed to groupLabelColorThreshold
groupGradientRadius
renamed to groupFillGradientRadius
groupOverlayOpacity
renamed to parentFillOpacity
groupFontFamily
renamed to groupLabelFontFamily
groupMaxTotalLabelHeight
renamed to groupLabelMaxTotalHeight
minGroupDiameter
renamed to groupMinDiameter
pixelRatioDuringRollout
renamed to wireframePixelRatio
onGroupOpenOrClose
renamed to onGroupOpenOrCloseChanged

Removed options

A number of options have been removed. Certain removed options have been replaced with other options that allow you to achieve the same and a range of similar effects. A number of options have been removed because they were not relevant in the updated layout and rendering engine.

backgroundColor

as of FoamTree 3.0.0 incremental group redrawing is performed on a much larger scale. Explicit drawing of background color would prevent FoamTree from incremental visualization updates and thus slow down rendering.

You can modify the background of the visualization by applying CSS styling to the HTML element in which FoamTree is embedded .

groupGradientType
use the groupFillType to plain to disable gradients.
groupGradientStrength
use the groupFillGradientCenterLightnessShift and groupFillGradientRimLightnessShift to achieve similar effects.
groupSelectionColor
use the groupSelectionFillSaturationShift and groupColorDecorator to tune the fill color of selected groups.
groupHoverColor
use the groupHoverFillSaturationShift and groupColorDecorator to tune the fill color of hovered groups.
groupHoverOutlineColor
use the groupHoverStrokeSaturationShift and groupHoverStrokeLightnessShift options to tune the stroke color of hovered groups.
groupLinePadding
renamed to groupLabelLineHeight, which now behaves similar to the CSS line-padding property.
groupMinFontSize
to groupLabelMinFontSize. Note: as of FoamTree 3.0.0 only pixel-based values are supported. You can apply percentage-based scaling using the groupLabelMaxTotalHeight option.
groupMaxFontSize
to groupLabelMaxFontSize. Note: as of FoamTree 3.0.0 only pixel-based values are supported. You can apply percentage-based scaling using the groupLabelMaxTotalHeight option.
rolloutType
you can now create your own rollout effects using the rollout options. Please see the API reference or the settings panel for a number of predefined effects.
rolloutSpeed
use rolloutDuration to determine the duration of the initial animation.
rolloutRate
use rolloutLabelDrag options to determine how quickly subsequent groups are included in the initial animation.
siblingRevealingParallelism
you can use the rolloutChildGroupsDrag option to control the sibling revealing parallelism.
childRevealingParallelism
you can use the rolloutChildGroupsDelay option to control the child group revealing parallelism.
fadeInTime
fading-in time is now controlled by the overall rolloutDuration option. To use a simple fade-in effect instead of the animated rollout, see the fadeDuration option.
fadeOutTime
fading-out time is now controlled by the overall pullbackDuration option. To use a simple fade-out effect instead of the animated pullback, see the fadeDuration option.
drawLabelsDuringRollout
use the wireframeLabelDrawing option to control label drawing during animations.
onBeforeSelection
you can now prevent the selection action directly in the onGroupClick event.
onBeforeOpenOrClose
you can now prevent the group opening action directly in the onGroupHold event.
roundBordersDuringRollout
with improved rendering speed, this option is not relevant any more.
useFastRenderingRoutines
not relevant as of FoamTree 3.0.0
aspectRatioImprovementSteps
not relevant as of FoamTree 3.0.0
groupWeightScaling
not relevant as of FoamTree 3.0.0
groupsVisibleAtRolloutStart
not relevant as of FoamTree 3.0.0
explodeDistance
not relevant as of FoamTree 3.0.0

Version 2.0.4

released on 04.09.2013

Improvements

  • Added support for altKey, shiftKey and other key modifiers to the object passed to all mouse or touch callback events.
  • onGroupClick callback provides group property for the topmost group one clicked on.

Bug fixes

  • FoamTree cells may move around (in a cycle) on redraw.

Version 2.0.3

released on 06.03.2013

Improvements

  • A number of events have been added to make it possible to change the default user interaction mappings, such as selecting groups on an event other than single mouse click.

Bug fixes

  • A JavaScript error would be thrown when setting or getting unknown option. Release 2.0.3 fixes this issue and logs the appropriate warning to the browser console.

Version 2.0.2

released on 06.03.2013

Improvements

  • The groupWeightScaling option has been added to influence the relative sizes of group polygons.
  • Versions prior to 2.0.2 would not draw selection outline when groupBorderWidth was smaller than 2. Version 2.0.2 draws selection outline for all group border widths.

Bug fixes

  • Pinch-in and -out gestures would not always open/close the desired group when the FoamTree HTML element was absolutely positioned. Release 2.0.2 fixes this issue.
  • When a number of groups had the same weight, FoamTree might needlessly change the layout of polygons during redraw. As of release 2.0.2 the layout remains stable if the size of the visualization is not changing.
  • The onRedraw callback would not be called after new data set was loaded. Release 2.0.2 fixes this problem.
  • Options object passed to the set method could get modified by FoamTree. Release 2.0.2 will not modify objects passed to the set method.
  • Options objects passed to the decorator callbacks, such as groupColorDecorator contained obfuscated option names. As of release 2.0.2, the callbacks will receive options object with original option names.
  • Color variables in decorator callbacks, such as groupColorDecorator, now support CSS3 color strings in hex, hsl, hsla, rgb and rgba models in addition to the object-based color specification.
  • On Mac OS, ⌘+click brings up context menu instead of adding new groups to the current selection. Release 2.0.2 fixes this issue.
  • The group property was undefined in groupLabelDecorator for the attribution group. Release 2.0.2 returns the { attribution: true } object in that property.

Deprecations

  • As of version 2.1.0, the property in which the onGroupSelectionChanged event exposes the selected groups will be renamed from selectedGroups to groups. Throughout 2.0.x line, the deprecated property selectedGroups will be maintained as an alias in addition to the new property.
  • As of version 2.1.0, the labelColor variable affecting group label color will be moved from groupLabelDecorator to groupColorDecorator. To allow rapid migration and to preserve compatibility, the 2.0.x line will handle labelColor changes both in groupLabelDecorator and groupColorDecorator.

Version 2.0.1

released on 08.08.2012

Improvements

  • The 2.0.1 release introduces support for high-density screens, such as Retina. You can use the pixelRatio option to set the physical-to-display pixel ratio FoamTree should assume.
  • As of the 2.0.1 release, group weights become optional in the dataObject specification. Groups with unspecified weights will be assumed to have the weight equal to 1.0.

Bug fixes

  • Opening a group by double click would change the selection state of the group being open. Release 2.0.1 fixes this issue.
  • Adding a CSS border to the FoamTree container element might create a blurry visualization image. Release 2.0.1 fixes this issue.
  • FoamTree optimized rendering routines would produce incorrect results on certain mobile devices, most likely caused by the browser bugs. Until the bugs get fixed, as of version 2.0.1 FoamTree defaults to slower but correct rendering routines on those devices. Please see the useFastRenderingRoutines option for more details.
  • In certain cases the initial layout generated by FoamTree might be unstable and change after the first call to redraw. Version 2.0.1 fixes the issue.
  • FoamTree would not redraw groups without ids on programmatic changes of selection and expansion. Version 2.0.1 fixes the issue.
  • FoamTree would throw a JavaScript error when selection is programmatically changed immediately after a new data model has been set. Version 2.0.1 fixes the issue.

Version 2.0.0

released on 25.06.2012

Initial release of FoamTree for HTML5.