from .charts_shape_line_path import svg_pair from .charts_shape_types import Arc from .math_sin_cos import sin_cos 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"