drawLine((int)round($px), (int)round($py), (int)round($x), (int)round($y), $color); [$px, $py] = [$x, $y]; } } } /** A ring with its gap of $gap radians centred on $angle left out. */ public static function ring(Renderer $r, float $cx, float $cy, float $radius, float $angle, float $gap, float $thickness, mixed $color): void { if ($gap >= 2 * M_PI) { return; } $from = $angle + $gap / 2; self::arc($r, $cx, $cy, $radius, $from, $from + 2 * M_PI - $gap, $thickness, $color); } /** * A fading trail of positions (oldest first) as shrinking dots of $color ([r, g, b]). * * @param list $points * @param array{0: int, 1: int, 2: int} $color */ public static function trail(Renderer $r, array $points, float $radius, array $color): void { $n = count($points); foreach ($points as $i => [$x, $y]) { $k = ($i + 1) / max(1, $n); $r->fillCircle((int)round($x), (int)round($y), (int)max(1, round($radius * $k)), [$color[0], $color[1], $color[2], (int)(200 * $k)]); } } /** '#rrggbb' -> [r, g, b] */ public static function rgb(string $hex): array { $hex = ltrim($hex, '#'); return [hexdec(substr($hex, 0, 2)), hexdec(substr($hex, 2, 2)), hexdec(substr($hex, 4, 2))]; } }