Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Ray
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
3 / 3
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 angleDegrees
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 toCss
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Value;
6
7/**
8 * `ray()` per CSS Motion Path 1 §2.2 — a path defined by an angle and a
9 * length rather than by geometry.
10 *
11 * Not a {@see BasicShape}: the spec makes `ray()` a separate
12 * `<offset-path>` alternative, and it is never valid where a
13 * `<basic-shape>` is expected (`clip-path`, `shape-outside`).
14 *
15 * The angle is a compass BEARING, not a mathematical angle: `0deg`
16 * points up and positive angles turn clockwise.
17 */
18final readonly class Ray extends Value
19{
20    public function __construct(
21        /** The ray's bearing. `Calc` when the author wrote `calc()`. */
22        public Value $angle,
23        public RaySize $size = RaySize::ClosestSide,
24        /**
25         * `contain` shortens the ray so the element stays inside its
26         * containing block (CSS Motion Path 1 §2.2).
27         */
28        public bool $contain = false,
29        /**
30         * `at <position>` origin, as resolved x / y components. Null
31         * means the origin comes from `offset-position` instead.
32         */
33        public ?Value $atX = null,
34        public ?Value $atY = null,
35    ) {}
36
37    public function angleDegrees(): ?float
38    {
39        return $this->angle instanceof Angle ? $this->angle->toDegrees() : null;
40    }
41
42    public function toCss(): string
43    {
44        // Canonical order per the spec's serialization rules: angle,
45        // then a non-default size, then `contain`, then `at <position>`.
46        $parts = [$this->angle->toCss()];
47        if ($this->size !== RaySize::ClosestSide) {
48            $parts[] = $this->size->value;
49        }
50        if ($this->contain) {
51            $parts[] = 'contain';
52        }
53        if ($this->atX !== null && $this->atY !== null) {
54            $parts[] = 'at ' . $this->atX->toCss() . ' ' . $this->atY->toCss();
55        }
56        return 'ray(' . implode(' ', $parts) . ')';
57    }
58}