Functional Weave
Code in Python

charts.shape@1.0.0

impl/python/line_path.py

1,474 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from typing import List, Sequence, Tuple

from .charts_shape_types import Point
from .text_format_decimal import format_decimal  ← from text.format-decimal ^1.0.0 · built alongside by fune


def line_path(points: Sequence[Point]) -> str:
    """A polyline as SVG path data, in d3.line's format ("M10,20L30,40").

    A null y ends the current subpath; a run of one point is "Mx,yZ", as d3
    closes it so a round line cap still shows a dot.
    """
    out = ""
    for run in point_runs(points):
        for i, (x, y) in enumerate(run):
            out += ("M" if i == 0 else "L") + svg_pair(x, y)
        if len(run) == 1:
            out += "Z"
    return out


# Shared with the other path functions of this group.


def svg_pair(x: float, y: float) -> str:
    """"x,y" with each number rounded to 2 places, trailing zeros dropped."""
    return format_decimal(x, 2, True, "") + "," + format_decimal(y, 2, True, "")


def point_runs(points: Sequence[Point]) -> List[List[Tuple[float, float]]]:
    """The unbroken runs of a series: a point with a null y separates them."""
    runs: List[List[Tuple[float, float]]] = []
    current: List[Tuple[float, float]] = []
    for p in points:
        if p.y is None:
            if current:
                runs.append(current)
            current = []
        else:
            # float() so integer inputs take the same binary64 path as the
            # other languages.
            current.append((float(p.x), float(p.y)))
    if current:
        runs.append(current)
    return runs