negativeSpace
Finds the open, axis-aligned rectangles inside bounds that lie entirely
outside the marks described by obstacles. The marks are treated as a single
solid region — the convex hull of every (padded) obstacle box — so a hole in
the middle of a ring of points is never returned, only the space around them.
Boxes in options.exclude are kept clear too, each on its own. Each
returned box is maximal (it cannot grow in any direction without leaving
bounds or touching the hull or an excluded box). Results are sorted largest area first, and the
output is deterministic for a given input.
Basic example
Section titled “Basic example”Finds the empty rectangles inside bounds that avoid the marks (blue). The marks are treated as one solid region, their convex hull, so only the space around them is returned, never a pocket between them. Every box is maximal and the list is sorted largest first; the three largest are shown in orange. Charts use this to tuck a legend into unused plot area.
import {negativeSpace} from "@d3plus/math";
const bounds = {x: 0, y: 0, width: 260, height: 200};
const marks = [
{x: 60, y: 50, width: 40, height: 40},
{x: 120, y: 90, width: 50, height: 30},
{x: 90, y: 140, width: 30, height: 30},
];
negativeSpace(bounds, marks).slice(0, 3);
Exclude
Section titled “Exclude”exclude lists extra boxes to keep clear of on their own, outside the hull, such as controls overlaid in a corner (grey). The free boxes now avoid that corner as well.
import {negativeSpace} from "@d3plus/math";
const bounds = {x: 0, y: 0, width: 260, height: 200};
const marks = [
{x: 60, y: 50, width: 40, height: 40},
{x: 120, y: 90, width: 50, height: 30},
{x: 90, y: 140, width: 30, height: 30},
];
negativeSpace(bounds, marks, {
exclude: [{x: 190, y: 0, width: 70, height: 40}],
}).slice(0, 3);
Padding
Section titled “Padding”padding keeps that many pixels clear around every mark, so the free boxes stop short of the hull. minWidth and minHeight drop boxes too small to be useful.
import {negativeSpace} from "@d3plus/math";
const bounds = {x: 0, y: 0, width: 260, height: 200};
const marks = [
{x: 60, y: 50, width: 40, height: 40},
{x: 120, y: 90, width: 50, height: 30},
{x: 90, y: 140, width: 30, height: 30},
];
negativeSpace(bounds, marks, {padding: 12}).slice(0, 3);
Parameters
import {negativeSpace} from "@d3plus/math";