CircularCenterRadius::produce()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 48
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 48
rs 9.125
cc 3
eloc 30
nc 2
nop 0
1
<?php
2
3
/*
4
 * This file is part of the CGI-Calc package.
5
 *
6
 * (c) Milos Tomic <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Cgi\Calc\Field;
13
14
use Cgi\Calc\Point;
15
use Cgi\Calc\Point\PointSet;
16
17
class CircularCenterRadius extends AbstractFieldProducer
18
{
19
    /** @var Point */
20
    private $center;
21
22
    /** @var int */
23
    private $radius;
24
25
    /** @var bool */
26
    private $fill;
27
28
    /**
29
     * @param Point                  $center
30
     * @param int                    $radius
31
     * @param ValueProvider|callable $valueProvider
32
     */
33
    public function __construct(Point $center, $radius, $fill, $valueProvider = null)
34
    {
35
        parent::__construct($valueProvider);
36
37
        if (false === is_int($radius) || $radius < 0) {
38
            throw new \InvalidArgumentException('Radius must be non-negative integer');
39
        }
40
41
        $this->center = $center;
42
        $this->radius = $radius;
43
        $this->fill = (bool) $fill;
44
    }
45
46
    /**
47
     * @return PointSet
48
     */
49
    public function produce()
50
    {
51
        /*
52
         * Bressenham's Midpoint Circle algorithm
53
         * http://stackoverflow.com/questions/1022178/how-to-make-a-circle-on-a-grid
54
         * http://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm
55
         */
56
        $d = 3 - (2 * $this->radius);
57
        $x = 0;
58
        $y = $this->radius;
59
60
        $result = new PointSet();
61
        do {
62
            $this->addRow(
63
                $result,
64
                new Point($this->center->getX() + $x, $this->center->getY() + $y),
65
                new Point($this->center->getX() - $x, $this->center->getY() + $y)
66
            );
67
68
            $this->addRow(
69
                $result,
70
                new Point($this->center->getX() - $x, $this->center->getY() - $y),
71
                new Point($this->center->getX() + $x, $this->center->getY() - $y)
72
            );
73
74
            $this->addRow(
75
                $result,
76
                new Point($this->center->getX() - $y, $this->center->getY() + $x),
77
                new Point($this->center->getX() + $y, $this->center->getY() + $x)
78
            );
79
80
            $this->addRow(
81
                $result,
82
                new Point($this->center->getX() - $y, $this->center->getY() - $x),
83
                new Point($this->center->getX() + $y, $this->center->getY() - $x)
84
            );
85
86
            if ($d < 0) {
87
                $d = $d + (4 * $x) + 6;
88
            } else {
89
                $d = $d + 4 * ($x - $y) + 10;
90
                --$y;
91
            }
92
            ++$x;
93
        } while ($x <= $y);
94
95
        return $result;
96
    }
97
98
    /**
99
     * @param PointSet $result
100
     * @param Point    $a
101
     * @param Point    $b
102
     */
103
    private function addRow(PointSet $result, Point $a, Point $b)
104
    {
105
        if ($a->getX() > $b->getX()) {
106
            $t = $a;
107
            $a = $b;
108
            $b = $t;
109
        }
110
        if ($this->fill) {
111
            foreach ($a->forXUpTo($b) as $x) {
112
                $this->addPoint($result, new Point($x, $a->getY()));
113
            }
114
        } else {
115
            $this->addPoint($result, $a);
116
            $this->addPoint($result, $b);
117
        }
118
    }
119
120
    /**
121
     * @param PointSet $result
122
     * @param Point    $point
123
     */
124
    private function addPoint(PointSet $result, Point $point)
125
    {
126
        $value = $this->getValue($point);
127
        $result->attach($point, $value);
128
    }
129
}
130