Completed
Branch master (42e482)
by Edgar
05:48 queued 02:06
created

Bezier::avoidDivisionByZero()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
namespace nstdio\svg\util;
3
4
/**
5
 * Class Bezier
6
 *
7
 * @package nstdio\svg\util
8
 * @author  Edgar Asatryan <[email protected]>
9
 */
10
class Bezier
11
{
12
    const EPSILON = 0.00001;
13
14
    public static function quadraticBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y)
15
    {
16
        $devx = $p0x + $p2x - 2 * $p1x;
17
        $devy = $p0y + $p2y - 2 * $p1y;
18
19
        self::avoidDivisionByZero($devy);
20
        self::avoidDivisionByZero($devx);
21
22
        $tx = ($p0x - $p1x) / $devx;
23
        $ty = ($p0y - $p1y) / $devy;
24
25
        self::checkBezierInterval($tx);
26
        self::checkBezierInterval($ty);
27
28
        $txByTX = $p0x * pow(1 - $tx, 2) + 2 * $tx * $p1x * (1 - $tx) + $p2x * $tx * $tx;
29
        $tyByTX = $p0y * pow(1 - $tx, 2) + 2 * $tx * $p1y * (1 - $tx) + $p2y * $tx * $tx;
30
31
        $txByTY = $p0x * pow(1 - $ty, 2) + 2 * $ty * $p1x * (1 - $ty) + $p2x * $ty * $ty;
32
        $tyByTY = $p0y * pow(1 - $ty, 2) + 2 * $ty * $p1y * (1 - $ty) + $p2y * $ty * $ty;
33
34
        $x1 = min($txByTX, $p0x, $p2x, $txByTY);
35
        $y1 = min($tyByTX, $p0y, $p2y, $tyByTY);
36
37
38
        $x2 = max($txByTX, $p0x, $p2x, $txByTY);
39
        $y2 = max($tyByTX, $p0y, $p2y, $tyByTY);
40
41
42
        return [
43
            'width'  => $x2 - min($x2, $x1),
44
            'height' => max($y2, $y1) - min($y2, $y1),
45
            'x'      => min($x2, $x1),
46
            'y'      => min($y2, $y1),
47
        ];
48
    }
49
50
    public static function cubicBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y, $p3x, $p3y)
51
    {
52
        $ax = $p3x - $p0x + 3 * ($p1x - $p2x);
53
        $bx = 2 * ($p0x - 2 * $p1x + $p2x);
54
        $cx = $p1x - $p0x;
55
56
        $ay = $p3y - $p0y + 3 * ($p1y - $p2y);
57
        $by = 2 * ($p0y - 2 * $p1y + $p2y);
58
        $cy = $p1y - $p0y;
59
60
        $txRoots = self::getRoots($ax, $bx, $cx);
61
        $tyRoots = self::getRoots($ay, $by, $cy);
62
63
        $tv0x = self::getCubicValue(0, $p0x, $p1x, $p2x, $p3x);
64
        $tv1x = self::getCubicValue($txRoots[0], $p0x, $p1x, $p2x, $p3x);
65
        $tv2x = self::getCubicValue($txRoots[1], $p0x, $p1x, $p2x, $p3x);
66
        $tv3x = self::getCubicValue(1, $p0x, $p1x, $p2x, $p3x);
67
68
        $tv0y = self::getCubicValue(0, $p0y, $p1y, $p2y, $p3y);
69
        $tv1y = self::getCubicValue($tyRoots[0], $p0y, $p1y, $p2y, $p3y);
70
        $tv2y = self::getCubicValue($tyRoots[1], $p0y, $p1y, $p2y, $p3y);
71
        $tv3y = self::getCubicValue(1, $p0y, $p1y, $p2y, $p3y);
72
73
74
        $x1 = min($tv0x, $tv1x, $tv2x, $tv3x, $p0x, $p3x);
75
        $y1 = min($tv0y, $tv1y, $tv2y, $tv3y, $p0y, $p3y);
76
77
        $x2 = max($tv0x, $tv1x, $tv2x, $tv3x, $p0x, $p3x);
78
        $y2 = max($tv0y, $tv1y, $tv2y, $tv3y, $p0y, $p3y);
79
80
        return [
81
            'width'  => $x2 - min($x2, $x1),
82
            'height' => max($y2, $y1) - min($y2, $y1),
83
            'x'      => min($x2, $x1),
84
            'y'      => min($y2, $y1),
85
        ];
86
87
    }
88
89
    public static function getRoots($a, $b, $c)
90
    {
91
        $dis = $b * $b - 4 * $a * $c;
92
        if ($dis < 0) {
93
            return null;
94
        }
95
        self::avoidDivisionByZero($a);
96
97
        $disSqrt = sqrt($dis);
98
        $root1 = (-$b + $disSqrt) / (2 * $a);
99
        $root2 = (-$b - $disSqrt) / (2 * $a);
100
101
        self::checkBezierInterval($root1);
102
        self::checkBezierInterval($root2);
103
104
        return [$root1, $root2];
105
    }
106
107
    private static function getCubicValue($t, $p0, $p1, $p2, $p3)
108
    {
109
        $omt = 1 - $t;
110
        $value = $p0 * $omt * $omt *$omt +
111
            3 * $p1 * $t * $omt * $omt +
112
            3 * $p2 * $t * $t * $omt +
113
            $p3 * $t * $t * $t;
114
115
        return $value;
116
    }
117
118
    /**
119
     * @param $value
120
     */
121
    private static function avoidDivisionByZero(&$value)
122
    {
123
        if ($value === 0) {
124
            $value = self::EPSILON;
125
        }
126
    }
127
128
    /**
129
     * @param $t
130
     */
131
    private static function checkBezierInterval(&$t)
132
    {
133
        if ($t > 1 || $t < 0) {
134
            $t = 0;
135
        }
136
    }
137
}
138