Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.12% covered (success)
94.12%
16 / 17
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
GradientCorner
94.12% covered (success)
94.12%
16 / 17
50.00% covered (danger)
50.00%
1 / 2
17.06
0.00% covered (danger)
0.00%
0 / 1
 angleFor
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
7.10
 fromSides
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
10
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Value;
6
7/**
8 * A `linear-gradient(to <corner>, …)` direction. Unlike side keywords
9 * (`to top` / `to right`), a corner direction's angle depends on the
10 * gradient box's aspect ratio: the gradient line is perpendicular to the
11 * diagonal joining the two *other* corners (CSS Images 3 §3.1). So the
12 * corner is preserved on the parsed value and resolved to a concrete
13 * angle at paint time, when the box size is known.
14 */
15enum GradientCorner
16{
17    case TopLeft;
18    case TopRight;
19    case BottomLeft;
20    case BottomRight;
21
22    /**
23     * The CSS gradient-line angle in degrees (0deg = to top, increasing
24     * clockwise) that points this corner for a `$width` × `$height` box.
25     *
26     * The gradient line is perpendicular to the diagonal joining the other
27     * two corners, so with `base = atan2(height, width)` (the top-right
28     * angle) the four corners are `base`, `180 − base`, `180 + base`,
29     * `360 − base`. A square (width == height) yields the familiar
30     * 45 / 135 / 225 / 315.
31     */
32    public function angleFor(float $width, float $height): float
33    {
34        // Degenerate box → fall back to the square-diagonal angles.
35        if ($width <= 0.0 || $height <= 0.0) {
36            $base = 45.0;
37        } else {
38            $base = rad2deg(atan2($height, $width));
39        }
40        return match ($this) {
41            self::TopRight => $base,
42            self::BottomRight => 180.0 - $base,
43            self::BottomLeft => 180.0 + $base,
44            self::TopLeft => 360.0 - $base,
45        };
46    }
47
48    /**
49     * Map a set of side keywords (already lower-cased) to a corner, or
50     * null when they don't name a corner (a single side or unrecognised).
51     *
52     * @param list<string> $sides
53     */
54    public static function fromSides(array $sides): ?self
55    {
56        $hasTop = in_array('top', $sides, true);
57        $hasBottom = in_array('bottom', $sides, true);
58        $hasLeft = in_array('left', $sides, true);
59        $hasRight = in_array('right', $sides, true);
60        return match (true) {
61            $hasTop && $hasLeft => self::TopLeft,
62            $hasTop && $hasRight => self::TopRight,
63            $hasBottom && $hasLeft => self::BottomLeft,
64            $hasBottom && $hasRight => self::BottomRight,
65            default => null,
66        };
67    }
68}