Completed
Push — master ( d97bf1...ae7c83 )
by ignace nyamagana
04:39
created

GanttChart::drawDataPortion()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 9.9666
c 0
b 0
f 0
cc 3
nc 1
nop 2
crap 3
1
<?php
2
3
/**
4
 * League.Period (https://period.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Period;
15
16
use function array_fill;
17
use function array_splice;
18
use function ceil;
19
use function count;
20
use function floor;
21
use function implode;
22
use function str_pad;
23
use function str_repeat;
24
25
/**
26
 * A class to output a Dataset via a Gantt Bar graph.
27
 */
28
final class GanttChart implements Chart
29
{
30
    /**
31
     * @var GanttChartConfig
32
     */
33
    private $config;
34
35
    /**
36
     * @var float
37
     */
38
    private $start;
39
40
    /**
41
     * @var float
42
     */
43
    private $unit;
44
45
    /**
46
     * New instance.
47
     *
48
     * @param ?GanttChartConfig $config
49
     */
50 3
    public function __construct(?GanttChartConfig $config = null)
51
    {
52 3
        $this->config = $config ?? new GanttChartConfig();
53 3
    }
54
55
    /**
56
     * @inheritDoc
57
     *
58
     * The generated Gantt Bar can be represented like the following but depends on the configuration used
59
     *
60
     * A       [--------)
61
     * B                    [--)
62
     * C                            [-----)
63
     * D              [---------------)
64
     * RESULT         [-)   [--)    [-)
65
     */
66 12
    public function stroke(Data $dataset): void
67
    {
68 12
        $this->setChartScale($dataset);
69 12
        $padding = $this->config->labelAlign();
70 12
        $gap = str_repeat(' ', $this->config->gapSize());
71 12
        $leftMargin = str_repeat(' ', $this->config->leftMarginSize());
72 12
        $lineCharacters = array_fill(0, $this->config->width(), $this->config->space());
73 12
        $labelMaxLength = $dataset->labelMaxLength();
74 12
        $colorCodeIndexes = $this->config->colors();
75 12
        $colorCodeCount = count($colorCodeIndexes);
76 12
        $output = $this->config->output();
77 12
        foreach ($dataset as $offset => [$label, $item]) {
78 9
            $colorIndex = $colorCodeIndexes[$offset % $colorCodeCount];
79 9
            $labelPortion = str_pad($label, $labelMaxLength, ' ', $padding);
80 9
            $dataPortion = $this->drawDataPortion($item, $lineCharacters);
81 9
            $output->writeln($leftMargin.$labelPortion.$gap.$dataPortion, $colorIndex);
82
        }
83 12
    }
84
85
    /**
86
     * Sets the scale to render the line.
87
     */
88 12
    private function setChartScale(Data $dataset): void
89
    {
90 12
        $this->start = 0;
91 12
        $this->unit = 1;
92 12
        $boundaries = $dataset->boundaries();
93 12
        if (null !== $boundaries) {
94 6
            $this->start = $boundaries->getStartDate()->getTimestamp();
95 6
            $this->unit = $this->config->width() / $boundaries->getTimestampInterval();
96
        }
97 12
    }
98
99
    /**
100
     * Convert a Dataset item into a graph data portion.
101
     *
102
     * @param string[] $lineCharacters
103
     */
104 9
    private function drawDataPortion(Sequence $item, array $lineCharacters): string
105
    {
106
        $reducer = function (array $lineCharacters, Period $period): array {
107 6
            $startIndex = (int) floor(($period->getStartDate()->getTimestamp() - $this->start) * $this->unit);
108 6
            $endIndex = (int) ceil(($period->getEndDate()->getTimestamp() - $this->start) * $this->unit);
109 6
            $periodLength = $endIndex - $startIndex;
110
111 6
            array_splice($lineCharacters, $startIndex, $periodLength, array_fill(0, $periodLength, $this->config->body()));
112 6
            $lineCharacters[$startIndex] = $period->isStartIncluded() ? $this->config->startIncluded() : $this->config->startExcluded();
113 6
            $lineCharacters[$endIndex - 1] = $period->isEndIncluded() ? $this->config->endIncluded() : $this->config->endExcluded();
114
115 6
            return $lineCharacters;
116 9
        };
117
118 9
        return implode('', $item->reduce($reducer, $lineCharacters));
119
    }
120
}
121