Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/tinker10/D3-Labeler

D3 plug-in for automatic label placement using simulated annealing.
https://github.com/tinker10/D3-Labeler

Last synced: about 2 months ago
JSON representation

D3 plug-in for automatic label placement using simulated annealing.

Awesome Lists containing this project

README

        

D3-Labeler
=========

A D3 plug-in for automatic label placement using simulated annealing that easily incorporates into existing D3 code, with syntax mirroring other D3 layouts.

[View a demo here](http://tinker10.github.io/D3-Labeler/).

Installation
------------

Download labeler.js. Include the plug-in within the relevant .html file with:
```html

```

Components of a labeling problem
--------------------------------

![label](label.png)

Each *label* corresponds to an *anchor point*. A *leader line* may be used to help with the correspondence between the *label* and *anchor point*. None of the elements may cross the *graph boundary*.

Usage
-----------------

To automatically place labels, users declare a labeler (simulated annealing) layout, input label and anchor positions, the figure boundaries, and the number of Monte Carlo sweeps for simulated annealing. The general pattern is as follows:
```javascript
var labels = d3.labeler()
.label(label_array)
.anchor(anchor_array)
.width(w)
.height(h)
.start(nsweeps);
```
The default settings are: w = 1, h = 1, and nsweeps = 1000. The default label_array and anchor_array are empty arrays. Here we describe each term in more detail.

d3.labeler()

Start by declaring a labeling layout, the same as declaring any other D3 layout.

labeler.label([label_array])

Each label has the following attributes:

* x - the *x*-coordinate of the label.
* y - the *y*-coordinate of the label.
* width - the *width* of the label (approximating the label as a rectangle).
* height - the *height* of the label (same approximation).
* name - the label text.

```javascript
var label_array = [{x: 10.2, y: 17.1, name: "Node 3", width: 18.0, height: 7.2}, ...]
```

Note that width and height can be easily measured using the SVG getBBox() method. The dimensions are used to calculate overlaps.

```javascript
var index = 0;
labels.each(function() {
label_array[index].width = this.getBBox().width;
label_array[index].height = this.getBBox().height;
index += 1;
});
```

labeler.anchor([anchor_array])

Each anchor has the following attributes:

* x - the *x*-coordinate of the anchor.
* y - the *y*-coordinate of the anchor.
* r - the anchor radius (assuming anchor is a circle).

```javascript
var anchor_array = [{x: 5.3, y: 12.0, r: 7}, {x: 16.8, y: 23.5, r: 7}, ...]
```

labeler.width(w)

labeler.height(h)

The width and height are used to set the boundary conditions so that labels do not go outside the width and height of the figure. More specifically, Monte Carlo moves in which the labels cross the boundaries are rejected. If they are not specified, both the width and height default to 1.

labeler.start(nsweeps)

Finally, we specify the number of Monte Carlo sweeps for the optimization and run the simulated annealing procedure. The default for nsweeps is 1000. Note that one Monte Carlo sweep means that on average, each label is translated or rotated once. To obtain the actual number of Monte Carlo steps taken, multiply the number of sweeps by the number of labels.

labeler.alt_energy(user_defined_energy)

This function is constructed for expert users. The quality of the configuration is closely related to the energy function. The default energy function includes general labeling preferences (details below) and is suggested for most users. However, a user may wish to define his or her own energy function to suit individual preferences.
```javascript
new_energy_function = function(index, label_array, anchor_array) {
var ener = 0;
// insert user-defined interaction energies here
return ener;
}
```
The newly constructed function must take as input an integer index, an array of labels label_array, and an array of anchors anchor_array. This function must also return an energy term that should correspond to the energy of a particular label, namely label_array[index]. One may wish calculate an energy of interaction for label_array[index] with all other labels and anchors.

labeler.alt_schedule(user_defined_schedule)

Similarly, an expert user may wish to include a custom cooling schedule used in the simulated annealing procedure. The default cooling schedule is linear.
```javascript
new_cooling_schedule = function(currT, initialT, nsweeps) {
// insert user-defined schedule here
return updatedT;
}
```
This function takes as input the current simulation temperature currT, the initial temperature initialT, and the total number of sweeps nsweeps and returns the updated temperature updatedT. The user defined functions can be included as follows:

```javascript
var labels = d3.labeler()
.label(label_array)
.anchor(anchor_array)
.width(w)
.height(h)
.alt_energy(new_energy_function)
.alt_schedule(new_cooling_schedule)
.start(nsweeps);
```

Default energy function details
-------------------------------

In order to distinguish between the quality of different configurations in our search space, we need construct a function which takes as input a label configuration and outputs a score indicating the quality of the placements. In a labeling problem, the inputs are themselves functions of various parameters such as the amount of overlaps, distances between labels and their corresponding anchor points, and various stylistic preferences. This function, often an energy (also called cost or objective) function, is what we need to optimize. The default energy function includes penalties for:

* Label-label overlaps
* Label-anchor overlaps
* Labels far from the corresponding anchor
* Leader line intersections
* Poorly oriented labels

Author
------
* Evan Wang ()