Utilities
The pydreamplet.utils module contains small helpers used by colors, generators,
scales, charts, and label layout.
Utilities are imported from pydreamplet.utils.
from pydreamplet.utils import calculate_ticks, place_labels_1d
Visual Example
This example uses place_labels_1d() to resolve overlapping labels while keeping
each label close to its original anchor.
import pydreamplet as dp
from pydreamplet.utils import place_labels_1d
anchors = [42, 55, 68, 128, 136, 206]
sizes = [36, 36, 36, 44, 44, 38]
placements = place_labels_1d(anchors, sizes, gap=4, bounds=(24, 276))
svg = dp.SVG(300, 150)
svg.append(dp.Line(24, 114, 276, 114, stroke="currentColor", opacity=0.3))
for anchor, placement in zip(anchors, placements):
svg.append(
dp.Line(anchor, 114, placement.position, 78, stroke="currentColor", opacity=0.35),
dp.Circle(cx=anchor, cy=114, r=3, fill="#f83898"),
dp.Rect(
x=placement.start,
y=60,
width=placement.size,
height=24,
rx=4,
fill="#14b8a6",
opacity=0.24,
stroke="#14b8a6",
stroke_width=1.5,
),
dp.Text(
str(int(anchor)),
x=placement.position,
y=76,
font_size=11,
text_anchor="middle",
fill="currentColor",
),
)
svg.append(dp.Text("anchor positions", x=24, y=134, font_size=12, fill="currentColor"))
svg.append(dp.Text("resolved label positions", x=58, y=45, font_size=12, fill="currentColor"))
Numeric Helpers
math_round(x: Real) -> int
Rounds with half-up behavior by returning int(x + 0.5).
from pydreamplet.utils import math_round
assert math_round(3.4) == 3
assert math_round(3.6) == 4
constrain(value: Real, min_val: Real, max_val: Real) -> Real
Clamps value into the inclusive range [min_val, max_val].
from pydreamplet.utils import constrain
assert constrain(10, 0, 5) == 5
assert constrain(-3, 0, 5) == 0
radians(degrees: Real) -> Real
degrees(radians: Real) -> Real
Converts between degrees and radians.
from pydreamplet.utils import degrees, radians
assert radians(180) == 3.141592653589793
assert degrees(3.141592653589793) == 180.0
Ticks
calculate_ticks(
min_val: Real,
max_val: Real,
num_ticks: int = 5,
below_max: bool = True,
) -> list[Real]
Returns rounded tick values using 1, 2, 5, or 10 times a power-of-ten step.
min_val must be less than max_val. When below_max is True, ticks above
the maximum are removed.
from pydreamplet.utils import calculate_ticks
assert calculate_ticks(0, 42986, 5) == [0, 10000, 20000, 30000, 40000]
assert calculate_ticks(0, 1, 5) == [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
assert calculate_ticks(0, 42986, 3, below_max=False) == [0, 20000, 40000, 60000]
Pie Angles
pie_angles(
values: Sequence[Real],
start_angle: Real = 0,
end_angle: Real | None = None,
) -> list[tuple[float, float]]
Splits an angular span proportionally by values. If end_angle is omitted,
the span is a full turn from start_angle to start_angle + 360.
from pydreamplet.utils import pie_angles
assert pie_angles([1, 2, 3]) == [(0, 60), (60, 180), (180, 360)]
assert pie_angles([1, 2, 3], start_angle=90) == [(90, 150), (150, 270), (270, 450)]
assert pie_angles([]) == []
A list whose sum is zero raises ZeroDivisionError.
Sampling
sample_uniform(
input_list: list[Any],
n: int,
precedence: Literal["first", "last"] | None = "first",
) -> tuple[int, ...]
Returns evenly spaced indices. precedence="first" anchors the first index,
"last" anchors the last index, and None chooses balanced interior indices.
from pydreamplet.utils import sample_uniform
items = list(range(10))
assert sample_uniform(items, n=4, precedence="first") == (0, 3, 6, 9)
assert sample_uniform(items, n=3, precedence="last") == (1, 5, 9)
assert sample_uniform(list(range(12)), n=4, precedence=None) == (1, 3, 7, 10)
Invalid precedence values raise ValueError.
Label Layout
force_distance(values: Sequence[Real], distance: Real) -> list[Real]
Adjusts unsorted numeric positions so adjacent sorted positions are at least
distance apart, then returns results in the original input order.
from pydreamplet.utils import force_distance
positions = force_distance([2, 6, 7, 8, 10, 16, 18], distance=2)
assert positions == [2, 5, 7, 9, 11, 16, 18]
resolve_collisions_1d(
anchors: Sequence[Real],
sizes: Sequence[Real],
*,
gap: Real = 0,
bounds: tuple[Real, Real] | None = None,
) -> list[float]
Resolves centered 1D items so their extents do not overlap. anchors and
sizes must have the same length. gap and every size must be non-negative.
from pydreamplet.utils import resolve_collisions_1d
assert resolve_collisions_1d([0, 1, 10], [4, 4, 4], gap=1) == [0, 5, 10]
assert resolve_collisions_1d([5, 0, 1], [4, 4, 4], gap=1) == [10, 0, 5]
place_labels_1d(
anchors: Sequence[Real],
sizes: Sequence[Real],
*,
gap: Real = 0,
bounds: tuple[Real, Real] | None = None,
) -> list[LabelPlacement]
Wraps resolve_collisions_1d() and returns LabelPlacement records.
from pydreamplet.utils import place_labels_1d
placements = place_labels_1d([0, 1], [4, 4], gap=1)
assert [placement.position for placement in placements] == [0, 5]
assert placements[0].start == -2
assert placements[0].end == 2
LabelPlacement is a frozen dataclass with anchor, position, and size
fields plus computed start and end properties.
Bounding Boxes
bboxes_overlap(a: BoundingBox, b: BoundingBox, padding: Real = 0) -> bool
Returns True when two BoundingBox objects overlap. Touching edges are not
considered overlapping. Positive padding expands both boxes before testing.
from pydreamplet import BoundingBox
from pydreamplet.utils import bboxes_overlap
left = BoundingBox(0, 0, 10, 10)
right = BoundingBox(10, 0, 5, 5)
assert bboxes_overlap(left, right) is False
assert bboxes_overlap(left, right, padding=0.1) is True
Negative padding raises ValueError.