d3¶
d3 is another popular DOM manipulation library available in JavaScript. This is the domonic-side Python port of that surface.
Ported modules: selection, scale, shape, array, time,
hierarchy, interpolate, color, format, dispatch, path,
polygon, timer, tile and queue. Not yet ported: axis,
brush, chord, contour, delaunay, drag, dsv, ease,
force, geo, quadtree, random, scale-chromatic,
time-format, transition and zoom.
It has several utilities for working with the DOM.
selection¶
d3’s selection API is very powerful.
from domonic.dom import *
from domonic.html import *
from domonic.d3.selection import *
import domonic.d3 as d3
Selections support data binding with update, enter, and exit groups:
from domonic.d3.selection import select
from domonic.html import li, ul
items = ul(li(), li())
update = select(items).selectAll("li").data(["one", "two", "three"])
update.enter().append("li").text(lambda data: data)
Build a Small Data List¶
from domonic.d3.selection import select
from domonic.html import div
root = div(_id="chart")
select(root).selectAll("span").data([10, 20, 30]).enter().append("span").text(
lambda value: f"{value}%"
)
print(root)
# <div id="chart"><span>10%</span><span>20%</span><span>30%</span></div>
format¶
d3 includes helpers for formatting numbers and avoiding floating-point display noise.
from domonic.dom import *
from domonic.html import *
from domonic.d3.format import *
from domonic.d3.format import format
dispatch¶
d3 has an event system.
from domonic.dom import *
from domonic.html import *
from domonic.d3.dispatch import dispatch, Dispatch
scale¶
Scales map a data domain onto a visual range. Continuous, sequential,
diverging, quantise, quantile, threshold, ordinal, band, point, time and UTC
scales are ported.
from domonic.d3.scale import scaleLinear, scaleBand, scaleOrdinal
x = scaleLinear().domain([0, 100]).range([0, 960])
x(50) # 480.0
x.ticks(5) # [0, 20, 40, 60, 80, 100]
band = scaleBand().domain(["a", "b", "c"]).range([0, 300]).padding(0.1)
band("b") # 106.45...
band.bandwidth() # 87.09...
colour = scaleOrdinal().domain(["a", "b"]).range(["red", "blue"])
colour("a") # "red"
Also available: scaleLog, scalePow / scaleSqrt, scaleSymlog,
scaleRadial, scaleQuantize, scaleQuantile, scaleThreshold,
scalePoint, scaleSequential, scaleDiverging, scaleTime and
scaleUtc.
shape¶
Generators that turn data into SVG path strings: line, area, arc,
pie, stack, link and symbol, plus the curve* interpolators.
from domonic.d3.shape import line, arc, pie
draw = line().x(lambda d, *_: d[0]).y(lambda d, *_: d[1])
draw([(0, 0), (1, 1), (2, 4)]) # "M0,0L1,1L2,4"
wedge = arc().innerRadius(0).outerRadius(100)
wedge({"startAngle": 0, "endAngle": 1.57})
slices = pie()([1, 1, 2]) # three arc-datum dicts
The angle and radius accessors of arc() default to reading startAngle /
endAngle / innerRadius / outerRadius off the datum, matching d3.
array¶
Statistics, search, and data-reshaping helpers over iterables.
from domonic.d3.array import extent, mean, ticks, group, bin
extent([3, 1, 2]) # [1, 3]
mean([1, 2, 3]) # 2.0
ticks(0, 10, 5) # [0, 2, 4, 6, 8, 10]
group([{"t": "a"}, {"t": "b"}, {"t": "a"}], lambda d: d["t"])
Includes min / max / sum / median / quantile /
variance / deviation, bisector and bisect, range /
tickStep / nice, group(s) / rollup(s) / index(es),
cross / merge / pairs / transpose / zip, the set helpers
(union, intersection, difference, …), and bin().
time¶
TimeInterval objects for flooring, ceiling, offsetting and ranging over
datetime values, in local time and UTC.
import datetime
from domonic.d3.time import timeDay, timeWeek, timeMonth
when = datetime.datetime(2026, 3, 15, 12, 30)
timeDay.floor(when) # 2026-03-15 00:00:00
timeWeek.range(timeMonth.floor(when), when)
timeSecond .. timeYear and the weekday intervals are provided, each
with a utc* counterpart, plus timeTicks / timeTickInterval.
hierarchy¶
Turn nested (or flat, via stratify) data into a tree of Node objects
and lay it out.
from domonic.d3.hierarchy import hierarchy, tree
root = hierarchy({
"name": "root",
"children": [{"name": "a"}, {"name": "b", "children": [{"name": "c"}]}],
})
root.height # 2
[n.data["name"] for n in root.leaves()] # ["a", "c"]
tree().size([400, 200])(root) # assigns node.x / node.y
Layouts: tree / cluster, partition, treemap (with
treemapSquarify / Dice / Slice / SliceDice / Binary) and
pack.
interpolate¶
Interpolators between two values, returning a function of t in [0, 1].
from domonic.d3.interpolate import interpolate, interpolateRgb
interpolate(0, 100)(0.25) # 25.0
interpolateRgb("red", "blue")(0.5) # "rgb(128, 0, 128)"
Covers numbers, rounds, strings (with embedded numbers), dates, arrays,
objects, basis splines, the colour-space interpolators
(interpolateRgb / Hsl / Lab / Hcl / Cubehelix, with
*Long hue variants), plus piecewise, quantize and
interpolateZoom.
color¶
Parse and manipulate colours in RGB, HSL, Lab, HCL and Cubehelix space.
from domonic.d3.color import color, rgb
color("#ff0000") # rgb(255, 0, 0)
color("steelblue").formatHex()
rgb(255, 0, 0).darker(1)
path¶
d3’s path API converts SVG paths to a d3.path object compatible with canvas.
from domonic.svg import *
from domonic.d3.path import Path
polygon¶
d3 has a shape API.
from domonic.d3.polygon import *
D3 expects a polygon to be a 2D array of integers listed counterclockwise, though clockwise works too. This is not the same as SVG’s polygon element. SVG polygons have a points method that you can parse into a 2D array and use here.
polygonArea() finds the area of a given polygon. It returns a float.
from domonic.d3.polygon import polygonArea
irreg_0 = [[5,11],[12,4],[7,7],[6,1]]
square = [[0,4],[4,4],[4,0],[0,0]]
triangle = [[-4,0],[0,4],[2,0]]
print(polygonArea(irreg_0)) # 15.0
print(polygonArea(square)) # 16.0
print(polygonArea(triangle)) # 12.0
polygonCentroid() finds the center of mass of a polygon. It returns two
floats in an array, representing the polygon’s center point.
from domonic.d3.polygon import polygonCentroid
irreg = [[-4,0],[8,12],[4,8],[-4,-4],[0,0]]
square = [[0,4],[4,4],[4,0],[0,0]]
print(polygonCentroid(irreg)) # [-0.0, 4.0]
print(polygonCentroid(square)) # [2.0, 2.0]
polygonHull() takes a 2D array of points and determines the hull using Andrew’s monotone chain algorithm. It returns the hull as a 2D array of points.
from domonic.d3.polygon import polygonHull
points = [[0,6],[12,8],[23,-5],[-5,-3],[5,11],[12,4],[7,7],[6,1]]
print(polygonHull(points))
# [[-5, -3], [0, 6], [5, 11], [12, 8], [23, -5]]
polygonContains() takes a polygon and a point. It returns True if the point is inside the polygon and False otherwise.
from domonic.d3.polygon import polygonContains
square = [[0,4],[4,4],[4,0],[0,0]]
print(polygonContains(square, [2, 2])) # True
print(polygonContains(square, [-2, -2])) # False
polygonLength() takes a polygon and returns the sum of its side lengths.
from domonic.d3.polygon import polygonLength
irreg_0 = [[5,11],[12,4],[7,7],[6,1]]
square = [[0,4],[4,4],[4,0],[0,0]]
print(round(polygonLength(irreg_0), 3)) # 31.863
print(polygonLength(square)) # 16.0