Functional Weave
Code in Rust

charts.shape@1.0.1

impl/python/arc_path.py

2,022 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 .charts_shape_line_path import svg_pair  ← linePath, another function of this group · built into the same file, even by a slim install
from .charts_shape_types import Arc
from .math_sin_cos import sin_cos  ← from math.sin-cos ^1.0.0 · built alongside by fune

PI = 3.141592653589793
TAU = 6.283185307179586


def _arc_point(arc: Arc, r: float, angle: float) -> str:
    sc = sin_cos(angle)
    return svg_pair(float(arc.cx) + r * sc.sin, float(arc.cy) - r * sc.cos)


def _arc_to(arc: Arc, r: float, large: int, sweep: int, angle: float) -> str:
    return "A" + svg_pair(r, r) + ",0," + str(large) + "," + str(sweep) + "," + _arc_point(arc, r, angle)


def arc_path(arc: Arc) -> str:
    """An annular sector as SVG path data. Angles are radians from 12 o'clock,
    clockwise, as in d3.arc. See the README for the exact path format."""
    ri = float(arc.inner_radius)
    ro = float(arc.outer_radius)
    a0 = float(arc.start_angle)
    a1 = float(arc.end_angle)
    if not ri >= 0 or not ro >= ri:
        raise ValueError(
            "arc radii must satisfy 0 <= innerRadius <= outerRadius, received %r and %r" % (arc.inner_radius, arc.outer_radius)
        )
    span = a1 - a0
    if span == 0 or ro == 0:
        return ""
    sweep = 1 if span > 0 else 0
    back = 1 - sweep
    direction = 1.0 if span > 0 else -1.0
    if abs(span) >= TAU:
        # One SVG arc cannot start and end at the same point, so a full turn is
        # two half turns; the hole is drawn the other way so it stays empty.
        half = a0 + direction * PI
        out = "M" + _arc_point(arc, ro, a0) + _arc_to(arc, ro, 1, sweep, half) + _arc_to(arc, ro, 1, sweep, a0) + "Z"
        if ri > 0:
            other = a0 - direction * PI
            out += "M" + _arc_point(arc, ri, a0) + _arc_to(arc, ri, 1, back, other) + _arc_to(arc, ri, 1, back, a0) + "Z"
        return out
    large = 1 if abs(span) > PI else 0
    out = "M" + _arc_point(arc, ro, a0) + _arc_to(arc, ro, large, sweep, a1)
    if ri > 0:
        out += "L" + _arc_point(arc, ri, a1) + _arc_to(arc, ri, large, back, a0)
    else:
        out += "L" + svg_pair(float(arc.cx), float(arc.cy))
    return out + "Z"