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
.
Quick start
You can get your first FoamTree visualization in 3 simple steps:
-
Include
carrotsearch.foamtree.js
in your page. - Define an HTML element in which FoamTree should be embedded. The element must have non-zero dimensions.
-
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>
Introduction
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:
- Google Chrome: two latest versions
- Mozilla Firefox: two latest versions
- Microsoft Internet Explorer: version 9 and later
- Mac OS Safari: version 5 and later
- iOS Safari: version 4.2 and later
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:
-
Upload your FoamTree license file at https://secure.carrotsearch.com.
-
Copy the URL of the "npm" link under the FoamTree version you'd like to install.
-
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 yourpackage.json
. -
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.
Basic usage
Embedding
Before FoamTree can display anything, it needs to be embedded in your HTML page. To embed FoamTree, your page needs to:
-
Load
carrotsearch.foamtree.js
, which contains FoamTree implementation. You will usually use a dedicatedscript
tag for this, but you can also concatenate FoamTree code with some other JavaScript on your page to speed up page loading. - Define the HTML element that will contain FoamTree visualization. FoamTree will occupy the full width and height of the element.
-
Inspect the static
supported
property of the visualization class to make sure the browser supports FoamTree. -
Initialize FoamTree by calling the
CarrotSearchFoamTree
constructor and providing the options object as a parameter. The only required option during initialization is theid
or theelement
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 thedataObject
to display or some visual customizations. See the options reference for a complete list of options.
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>
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");
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.
window.clearTimeout(timeout);
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
andexposure
options. -
By calling the
select
,open
andexpose
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
andexposed
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" } ] } ]});
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
andoff
methods, to add and remove event listeners, respectively. As opposed to using theset
method, theon
andoff
methods preserve the previously registered listeners.Both the
on
andoff
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
methodEvent name for the on
/off
methodsonGroupSelectionChanged 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); }; })());
window["_resizeHook"] = (function() { var timeout; return function() { window.clearTimeout(timeout); timeout = window.setTimeout(foamtree.resize, 300); }; })(); window.addEventListener("resize", window["_resizeHook"]);
window.removeEventListener("resize", window["_resizeHook"]); delete window["_resizeHook"];
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.
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 theattach
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
andpullbackDuration
to0
, 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
toordered
orsquarified
. 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 forrelaxationQualityThreshold
, starting from0.5
through1.0
up to10
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 settingrelaxationQualityThreshold
toNumber.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
toordered
orsquarified
. 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
totrue
. Additionally, you can cap the total relaxation time using therelaxationMaxDuration
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 themaxGroupLevelsDrawn
andmaxGroupLabelLevelsDrawn
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
is0
- when
groupBorderWidth
is less thangroupStrokeWidth
/ 2 + 0.5
-
when any group is selected and
groupBorderWidth
is less thangroupSelectionOutlineWidth
/ 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 istrue
, 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
andfinalIncrementalDrawMaxDuration
options. On high-DPI displays, you can also set separate pixel densities for the final and wireframe images using thepixelRatio
andwireframePixelRatio
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
andmaxGroupLabelLevelsDrawn
. -
Decrease the values of the
wireframeDrawMaxDuration
,finalCompleteDrawMaxDuration
andfinalIncrementalDrawMaxDuration
options, possibly to zero. -
Set
groupFillType
toplain
. -
Set
groupStrokeType
tonone
. -
Set
wireframeLabelDrawing
tonever
.
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
tohierarchical
for best performance. -
Decrease
groupMinDiameter
to0
, so that FoamTree keeps laying out child groups until the floating point computation precision limit is reached. -
Decrease
groupBorderWidth
andgroupInsetWidth
to allow more space for the child groups. -
Set
groupBorderRadius
to0
, 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.
Data structures
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 asselect
, use a number of additional properties provided in the object, such asselected
orkeepPrevious
.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 thedataObject
: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
ordragstart
- 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
andonGroupTransformEnd
events, for other events the scale is1
. - 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 to0
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.
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 providedargs
. The example below retrieves information about thegeometry
of the group with id1
. 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 desiredvalue
. If the providedoption
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" });
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 isgroupSelectionChanged
. - 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 isgroupSelectionChanged
. - 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 isfalse
. - 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
andall
properties:- selected
-
If
true
or not provided, the designated groups will get selected. Iffalse
, the designated groups will be deselected. - keepPrevious
-
If
true
or not provided, only the selection of the designated groups will be altered. Iffalse
, 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
andall
properties:- exposed
-
If
true
or not provided, the designated groups will get exposed. Iffalse
, the designated groups will be unexposed. - keepPrevious
-
If
true
, only the exposure of the designated groups will be altered. Iffalse
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
andall
properties:- open
-
If
true
or not provided, the designated groups will get open. Iffalse
, the designated groups will be closed. - keepPrevious
-
If
true
or not provided, only the state of the designated groups will be altered. Iffalse
, 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.
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:
-
In the
onModelChanging
event handler, setmaxGroupLevelsAttached
to1
. This will cause FoamTree to initialize only the top-level groups when new data is loaded. -
In the
onRolloutComplete
event handler, bring backmaxGroupLevelsAttached
to the default or desired value. Then, call theattach()
method for each top level group. Make sure to allow the main browser's thread to progress by, for example, making oneattach()
call per onerequestAnimationFrame()
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.
Options
Embedding
supported
A static property on CarrotSearchFoamTree
equal to true
if visualization
is supported on the current browser environment.
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.
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
oropen
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 is1.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 toalways
andstacking
ishierarchical
, 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
anddistanceFromCenter
. Please see theattributionPosition
andattributionDistanceFromCenter
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 inflattened
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
isfalse
, 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 therectangleAspectRatioPreference
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.
|
Regardless of the layout you choose, check also the stacking
option for different
ways of arranging hierarchical groups.
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 agroupLabelLayoutDecorator
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 toflattened
. - always
-
The extra label area will be drawn for all groups when
stacking
is set toflattened
. Ifstacking
is set tohierarchical
, each group that sets itsdescription
property totrue
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
andbottom
, 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 theget
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 benull
, 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 torelaxed
. The array is only returned iftrue
is passed as the third parameter of theget
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
andy
properties, passed as the third argument to theget
method. The coordinate space you will use here is the same as the on in which custom group content is drawn ingroupContentDecorator
.
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.
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.
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.
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
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
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.
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.
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.
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
andgeometry
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.
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
andgeometry
options. - variables
-
an object with two properties:
groupColor
, containing the group color computed by the default rainbow color model, andlabelColor
always equal toauto
. The callback function can either change some of the properties ofgroupColor
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
orrgba
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 ofauto
which will recompute the label color again depending on the updatedgroupColor
and visualization options controlling dark/ light label colors:labelColorThreshold
,labelLightColor
andlabelDarkColor
. -
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
.
running = false;
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 });
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
andgeometry
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 than1
mean the viewport is zoomed-out, a value of1
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
orundefined
, 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.
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.
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.
attributionLogo
The image to display in the attribution group. The image can be specified as a relative or absolute HTTP URL or a data URI. When using HTTP URLs, the image should to be preloaded before visualization is embedded, otherwise it may not be immediately visible. For this reason, the data URIs are recommended. You can use services like dataurl.net to convert your images to data URIs.
Since version 3.3.1, this 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.
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.
var logoImage = new Image(); logoImage.src = "../demos/assets/img/logo-stub.png"; logoImage.addEventListener("load", function() { // Trigger a reload to show the logo foamtree.set("attributionLogo", logoImage.src); foamtree.set("dataObject", foamtree.get("dataObject")); });
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.
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.
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.
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();
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
andgeometry
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 istrue
.This property is meaningful only when
groupContentDecoratorTriggering
isonSurfaceDirty
. If the triggering is doneonShapeDirty
, theshapeDirty
property willtrue
on all invocations of the decorator. - viewportScale
-
the current viewport scale. Values larger than
1
mean the viewport is zoomed-in, values smaller than1
mean the viewport is zoomed-out, a value of1
means the viewport is at its neutral zoom level.Using this property makes sense only when
groupContentDecoratorTriggering
isonSurfaceDirty
, 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 canfill()
,stroke()
orclip()
the polygon's shape. In most cases you'll set thegroupPolygonDrawn
variable tofalse
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 therectangleInPolygon
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
oronModelChanged
event listeners. See also thegroupContentDecoratorTriggering
option. -
Each group's elements are drawn in the following order:
-
group's polygon (unless the decorator sets the
groupPolygonDrawn
variable tofalse
) - the shapes drawn by the decorator
-
the default group's label (unless the decorator sets the
groupLabelDrawn
variable tofalse
)
-
group's polygon (unless the decorator sets the
- 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.
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 toalways
. - 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
andgeometry
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 thegroupBorderWidth
, you may occasionally see visual artifacts when using fast incremental updates. If this is the case, consider settingincrementalDraw
toaccurate
. - 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 thegroupBorderWidth
, 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 of1.0
means the viewport is in its neutral state. Values lower than1.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
orimage/jpeg
- quality
-
if
format
isimage/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 as2
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).
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({ groupSelectionOutlineColor: "red", groupSelectionOutlineWidth: 6, groupSelectionOutlineShadowSize: 4 })
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
andall
properties:- selected
-
If
true
or not provided, the designated groups will get selected. Iffalse
, the designated groups will get deselected. - keepPrevious
-
If
true
or not provided, only the state of the designated groups will be altered. Iffalse
, 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
andall
properties:- open
-
If
true
or not provided, the designated groups will get open. Iffalse
, the designated groups will get closed. - keepPrevious
-
If
true
or not provided, only the state of the designated groups will be altered. Iffalse
, 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
andall
properties:- exposed
-
If
true
or not provided, the designated groups will get exposed. Iffalse
, the designated groups will be unexposed. - keepPrevious
-
If
true
, only the exposure of the designated groups will be altered. Iffalse
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 ofmaxGroups
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);
Events
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.
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 requestedrelaxationQualityThreshold
has been achieved. - relaxationTimeout
-
When
true
, this is the last step of relaxation because therelaxationMaxDuration
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
groupContentDecorator
s.
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
andy
properties representing the coordinates of the point. - <return value>
-
an object representing the bounding box. The object contains the
x
andy
properties denoting the coordinates of the top-left corner of the rectangle, as well as thew
andh
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
andy
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
andy
properties denoting the coordinates of the centroid as well as thearea
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
andy
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 of1.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 than1.0
or lower than0.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
andy
properties denoting the coordinates of the top-left corner of the rectangle, as well as thew
andh
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
andy
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
andy
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
andy
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
and1
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 index0
will be the upper one, polygon at index1
will be the lower one. When the angle becomes 90 degrees, polygon at index0
will be on the left, polygon at index1
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).
-
Each polygon is represented by an array of objects representing the polygon's vertices;
each such object has the
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
andy
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 than1.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 theinvalidate
option totrue
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 isfalse
, 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
andh
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:
-
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 byfillPolygonWithText()
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); } } });
-
Caching expensive layout computations. When
groupContentDecoratorTriggering
isonSurfaceDirty
, 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 theshapeDirty
property of the decorator istrue
and whenshapeDirty
isfalse
, 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.
Release notes
Version 3.5.0
released on 19.01.2021The 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.2019The 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.2019The 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 thegroupLabelDrawn
variable tofalse
. Setting the variable totrue
would not work. Version 3.4.9 fixes the issue.
Version 3.4.8
released on 04.07.2019The 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.2019The 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 toflattened
and group hierarchy is deeper thanmaxGroupLevelsDrawn
, 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.2018The 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 standardwheel
events in other parts of the page. Version 3.4.6 fixes the issue by switching to listening to the standardwheel
event.
Documentation extensions
description
property-
Documentation of
description
property has been added to thehierarchy
option documentation.
Version 3.4.5
released on 26.07.2017The 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 theattach
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.2016The 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 thexAbsolute
andyAbsolute
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.
- 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 thegeometry()
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.2016The 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 toflattened
anddescriptionGroupPosition
equal tobottom
. 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 thegroupLabelMinFontSize
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.2015The 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.2015The 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 totrue
andgroupColorDecorator
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 fromstatic
), theposition
property would get reset after callingresize
. The 3.4.1 release fixes this issue.
Version 3.4.0
released on 11.09.2015The 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.
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
-
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
-
The Photo explorer demo visualizes Flickr's top daily photos. It uses the newly introduced
polygonContext
property ofgroupContentDecorator
to fill FoamTree polygons with bitmap images. - SCADA sensor dashboard
-
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 -
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 ofgroupContentDecorator
. 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 thedrawImage
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 to0
. 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 to0
. The 3.4.0 release fixes the issue. - initializer alias for
relaxationInitializer
does not work -
After renaming the
initializer
option torelaxationInitializer
, the 3.3.x and 3.4.x releases keepinitializer
as an alias torelaxationInitializer
. 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.2015The 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 thangroupBorderWidth
.
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.2015The 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 theImage
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 thehierarchy
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 toordered
orsquarified
. 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 theattributionPosition
documentation for more details.
Version 3.3.0
released on 16.02.2015The 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.
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
toordered
; to generate the ordered rectangular layout, setlayout
toordered
. In both cases, setlayoutByWeightOrder
tofalse
. 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 thehierarchy
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 whenlayout
is set torelaxed
. To emphasize this dependency, theinitializer
option has been renamed torelaxationInitializer
. With this change, all options that apply only whenlayout
isrelaxed
have therelaxation*
prefix.The 3.3.x and 3.4.x versions will keep
initializer
as an alias forrelaxationInitializer
. In 3.5.0, theinitializer
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.2014The 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
andonGroupDragEnd
to reliably disable panning of the visualization area. As of version 3.2.3, it is enough to prevent the default action ofonGroupDragStart
to achieve the same effect. Please see theviewport
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.2014The 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
andcontext.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 theonGroupHover
event. As a result, if the user pointer moves to the gap between groups, FoamTree will triggeronGroupHover
event with anundefined
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 tofalse
.Version 3.2.2 fixes this issue.
Version 3.2.1
released on 09.09.2014The 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
andattributionDistanceFromCenter
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.2014The 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. 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
is0
. This setting is particularly useful when visualizing large numbers of groups on one level.Also, you can use the
maxGroupLevelsDrawn
andmaxGroupLabelLevelsDrawn
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 loweredparentFillOpacity
, you may want to increase themaxGroupLevelsDrawn
to bring more group levels into view.
Version 3.1.0
released on 03.06.2014The 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. - 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.
- 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.
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 than0
, FoamTree would prevent the objects provided in thedataObject
option from being garbage collected even after some new data was subsequently set for rendering. Please note that the default value ofgroupGrowingDuration
is0
, 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 thebackgroundColor
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 thangroupLabelMinFontSize
.
Version 3.0.3
released on 10.04.2014The 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).
- 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
identifier0
-
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.2014The 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. TheindexByWeight
property is available in thegroupColorDecorator
callback and also directly through thehierarchy
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 theset
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.2014The 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.2014FoamTree 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.
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
andoff
methods to add and remove event listeners without affecting the other registered listeners. - promises
-
Groups can now be open and exposed using the
open
andexpose
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
andgeometry
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 asonGroupClick
.
Deprecations
onGroupSelectionChanged
-
The array of selected groups previously available in the
selectedGroups
property of theonGroupSelectionChanged
event details is now available as thegroups
property. groupLabelDecorator
-
FoamTree 2.0.x would expose the
labelColor
variable both ingroupLabelDecorator
andgroupColorDecorator
. As of version 3.0.0,labelColor
is only available ingroupColorDecorator
.
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
toplain
to disable gradients. groupGradientStrength
-
use the
groupFillGradientCenterLightnessShift
andgroupFillGradientRimLightnessShift
to achieve similar effects. groupSelectionColor
-
use the
groupSelectionFillSaturationShift
andgroupColorDecorator
to tune the fill color of selected groups. groupHoverColor
-
use the
groupHoverFillSaturationShift
andgroupColorDecorator
to tune the fill color of hovered groups. groupHoverOutlineColor
-
use the
groupHoverStrokeSaturationShift
andgroupHoverStrokeLightnessShift
options to tune the stroke color of hovered groups.
groupLinePadding
-
renamed to
groupLabelLineHeight
, which now behaves similar to the CSSline-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 thegroupLabelMaxTotalHeight
option. groupMaxFontSize
-
to
groupLabelMaxFontSize
. Note: as of FoamTree 3.0.0 only pixel-based values are supported. You can apply percentage-based scaling using thegroupLabelMaxTotalHeight
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 thefadeDuration
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 thefadeDuration
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.2013Improvements
-
Added support for
altKey
,shiftKey
and other key modifiers to the object passed to all mouse or touch callback events. onGroupClick
callback providesgroup
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.2013Improvements
- 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.2013Improvements
-
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 theset
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 ingroupLabelDecorator
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 fromselectedGroups
togroups
. Throughout 2.0.x line, the deprecated propertyselectedGroups
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 fromgroupLabelDecorator
togroupColorDecorator
. To allow rapid migration and to preserve compatibility, the 2.0.x line will handlelabelColor
changes both ingroupLabelDecorator
andgroupColorDecorator
.
Version 2.0.1
released on 08.08.2012Improvements
-
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
weight
s become optional in thedataObject
specification. Groups with unspecified weights will be assumed to have the weight equal to1.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
id
s 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.2012Initial release of FoamTree for HTML5.