├── .gitignore
├── examples
├── font
│ └── font.ttf
├── line.php
├── badInput.php
├── largeKeys.php
├── html.php
├── stretched.php
├── sinus.php
├── floats.php
├── realData.php
├── image.php
├── randomValues.php
└── example.php
├── phpstan.neon
├── src
├── Colorizers
│ ├── ColorException.php
│ ├── ColorizerInterface.php
│ ├── ImageColorizer.php
│ ├── HTMLColorizer.php
│ └── AsciiColorizer.php
├── Settings.php
├── Chart.php
└── Linechart.php
├── LICENCE.md
├── ecs.yml
├── composer.json
├── README.md
└── log
└── phpstan.html
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.idea/
3 |
--------------------------------------------------------------------------------
/examples/font/font.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noximo/PHP-colored-ascii-linechart/HEAD/examples/font/font.ttf
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | autoload_files:
3 | - vendor/autoload.php
4 | excludes_analyse:
5 | - %rootDir%/vendor
6 | ignoreErrors:
7 | checkMissingIterableValueType: false
8 |
--------------------------------------------------------------------------------
/src/Colorizers/ColorException.php:
--------------------------------------------------------------------------------
1 | setFPS(40);
11 |
12 | $lineGraph = new Linechart();
13 | $lineGraph->setSettings($settings);
14 |
15 | try {
16 | $linechart = new Linechart();
17 | $linechart->addMarkers([1, 1, 1, 1, 1, 1])->chart()->print();
18 | } catch (\Throwable $e) {
19 | echo $e->getMessage();
20 | }
21 |
--------------------------------------------------------------------------------
/examples/badInput.php:
--------------------------------------------------------------------------------
1 | 2,
13 | 9 => 2,
14 | 'a' => 12,
15 | 2 => 8,
16 | 15 => 4,
17 | 14 => 8,
18 | 8 => 2,
19 | 3 => 2,
20 | 4 => 2,
21 | ];
22 | $lineGraph->addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD], [AsciiColorizer::RED]);
23 |
24 | $lineGraph->chart()->print();
25 |
--------------------------------------------------------------------------------
/src/Colorizers/ImageColorizer.php:
--------------------------------------------------------------------------------
1 | 2,
13 | 14537 => 2,
14 | 14538 => 8,
15 | 14539 => 8,
16 | 14540 => 4,
17 | 14541 => 4,
18 | 14542 => 8,
19 | 14543 => 8,
20 | 14544 => 8,
21 | 14545 => 8,
22 | 14546 => 2,
23 | 14547 => 2,
24 | 14548 => 2,
25 | 14549 => 2,
26 | ];
27 | $lineGraph->addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD]);
28 |
29 | $lineGraph->chart()->print();
30 |
--------------------------------------------------------------------------------
/examples/html.php:
--------------------------------------------------------------------------------
1 | setColorizer(new HTMLColorizer());
13 | $lineA = [];
14 |
15 | for ($i = 0; $i < +120; $i++) {
16 | $lineA[] = 10 * sin($i * ((M_PI * 4) / 120));
17 | }
18 |
19 | $lineGraph->addLine(0, ['color:black'], Linechart::FULL_LINE);
20 | $lineGraph->addMarkers($lineA, ['color: green'], ['color: red']);
21 | $lineGraph->setSettings($settings);
22 |
23 | $lineGraph->chart()->print();
24 |
--------------------------------------------------------------------------------
/examples/stretched.php:
--------------------------------------------------------------------------------
1 | setFPS(40);
12 |
13 | $lineGraph = new Linechart();
14 | $lineGraph->setSettings($settings);
15 |
16 | try {
17 | $line = [];
18 | for ($i = 0; $i < 50; $i++) {
19 | $line[$i] = $i;
20 | }
21 |
22 | $lineGraph->addMarkers($line, [AsciiColorizer::GREEN], [AsciiColorizer::RED]);
23 |
24 | $lineGraph->chart()->clearScreen()->print()->wait();
25 | $lineGraph->clearAllMarkers();
26 | } catch (\Throwable $e) {
27 | echo $e->getMessage();
28 | }
29 |
--------------------------------------------------------------------------------
/examples/sinus.php:
--------------------------------------------------------------------------------
1 | setFPS(120);
13 |
14 | for ($y = 0; $y < 1; $y++) {
15 | $lineA = [];
16 | for ($i = $y; $i < $y + 120; $i++) {
17 | $lineA[] = 1 * sin($i * ((M_PI * 4) / 120));
18 | }
19 |
20 | $lineGraph->addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD], [AsciiColorizer::RED]);
21 |
22 | $lineGraph->setSettings($settings);
23 | $lineGraph->chart()->clearScreen()->print()->wait();
24 | $lineGraph->clearAllMarkers();
25 | }
26 |
--------------------------------------------------------------------------------
/LICENCE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Tomas Pospisil
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Colorizers/HTMLColorizer.php:
--------------------------------------------------------------------------------
1 | %s", $cssStyles, $text);
25 | }
26 |
27 | public function getEOL(): string
28 | {
29 | return '
';
30 | }
31 |
32 | public function processFinalText(string $text): string
33 | {
34 | $div = "
";
35 | $text = str_replace([' ', 'span '], [' ', 'span '], $text);
36 | $endDiv = '
';
37 |
38 | return $div . $text . $endDiv;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/floats.php:
--------------------------------------------------------------------------------
1 | setFPS(40);
12 | $settings->setHeight(30);
13 | $lineGraph = new Linechart();
14 | $lineGraph->setSettings($settings);
15 |
16 | try {
17 | $line = [];
18 | for ($i = 0; $i < 120; $i++) {
19 | $line[$i] = $lineA[$i - 1] ?? 1 + (lcg_value() * random_int(-1, 1));
20 | }
21 | for ($y = 0; $y < 1500; $y++) {
22 | array_shift($line);
23 | $line[] = end($line) + (lcg_value() * random_int(-1, 1));
24 | $lineGraph->addMarkers($line, [AsciiColorizer::GREEN], [AsciiColorizer::RED]);
25 |
26 | //$lineGraph->addLine($line[0] * 1.1, [AsciiColorizer::CYAN], Linechart::FULL_LINE);
27 |
28 | $lineGraph->chart()->clearScreen()->print()->wait();
29 | $lineGraph->clearAllMarkers();
30 | }
31 | } catch (\Throwable $e) {
32 | echo $e->getMessage();
33 | }
34 |
--------------------------------------------------------------------------------
/examples/realData.php:
--------------------------------------------------------------------------------
1 | setFPS(40);
12 | $lineGraph = new Linechart();
13 | $lineGraph->setSettings($settings);
14 |
15 | try {
16 | $line = [0.0208, 0.020858, 0.021, 0.021, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021056, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021124, 0.0214, 0.0215, 0.021436, 0.02149, 0.021488, 0.02149, 0.02145, 0.02145, 0.021406, 0.02145, 0.02145, 0.02145, 0.0214, 0.02145, 0.021487, 0.02149, 0.021482, 0.02148, 0.02148, 0.0215, 0.0215, 0.0215, 0.0215, 0.021499, 0.021473, 0.021454, 0.021497, 0.021489, 0.021454, 0.021705, 0.02151, 0.021513];
17 |
18 | $lineGraph->addMarkers($line, [AsciiColorizer::GREEN], [AsciiColorizer::RED]);
19 |
20 | $lineGraph->chart()->clearScreen()->print()->wait();
21 | $lineGraph->clearAllMarkers();
22 | } catch (\Throwable $e) {
23 | echo $e->getMessage();
24 | }
25 |
--------------------------------------------------------------------------------
/ecs.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/psr2.yaml' }
3 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/psr12.yaml' }
4 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/common.yaml' }
5 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/clean-code.yaml' }
6 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/php70.yaml' }
7 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/php71.yaml' }
8 | - { resource: '%vendor_dir%/symplify/easy-coding-standard/config/set/symplify.yaml' }
9 | services:
10 |
11 | parameters:
12 | skip:
13 | PhpCsFixer\Fixer\Operator\NotOperatorWithSuccessorSpaceFixer: ~
14 | Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer: ~
15 | Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer: ~
16 | SlevomatCodingStandard\Sniffs\Namespaces\ReferenceUsedNamesOnlySniff: ~
17 | Symplify\CodingStandard\Sniffs\DependencyInjection\NoClassInstantiationSniff: ~
18 | Symplify\CodingStandard\Sniffs\CleanCode\ForbiddenStaticFunctionSniff: ~
19 | PhpCsFixer\Fixer\Import\OrderedImportsFixer: ~
20 | Symplify\CodingStandard\Fixer\Commenting\BlockPropertyCommentFixer: ~
21 | Symplify\CodingStandard\Sniffs\ControlStructure\SprintfOverContactSniff: ~
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "noximo/php-colored-ascii-linechart",
3 | "description": "Pretty line graphs in your console, html or images",
4 | "keywords": [
5 | "chart",
6 | "graph",
7 | "linechart",
8 | "linegraph",
9 | "ascii",
10 | "marker",
11 | "stock"
12 | ],
13 | "type": "library",
14 | "authors": [
15 | {
16 | "name": "Tomas Pospisil",
17 | "email": "pospisilt@gmail.com"
18 | }
19 | ],
20 | "autoload": {
21 | "psr-4": {
22 | "noximo\\PHPColoredAsciiLinechart\\": "src/"
23 | }
24 | },
25 | "require": {
26 | "php": ">= 7.1",
27 | "ext-json": ">=1.3.7"
28 | },
29 | "license": "MIT",
30 | "require-dev": {
31 | "php-parallel-lint/php-parallel-lint": "^v1.0.0",
32 | "phpstan/phpstan": "^0.12",
33 | "phpstan/phpstan-deprecation-rules": "^0.12",
34 | "phpstan/phpstan-strict-rules": "^0.12",
35 | "roave/security-advisories": "dev-master",
36 | "symplify/easy-coding-standard": "^6.0"
37 | },
38 | "scripts": {
39 | "check-cs": "ecs check src examples",
40 | "fix-cs": "ecs check src examples --fix",
41 | "lint": "parallel-lint --colors --exclude vendor .",
42 | "phpstan": "phpstan analyze src examples --level max -c phpstan.neon",
43 | "test": [
44 | "@lint",
45 | "@check-cs",
46 | "@phpstan"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/examples/image.php:
--------------------------------------------------------------------------------
1 | setColorizer(new ImageColorizer());
14 | $lineA = [];
15 |
16 | for ($i = 0; $i < +120; $i++) {
17 | $lineA[] = 10 * sin($i * ((M_PI * 4) / 120));
18 | }
19 |
20 | $lineGraph->addMarkers($lineA, [AsciiColorizer::WHITE]);
21 |
22 | $lineGraph->setSettings($settings);
23 | $graph = $lineGraph->chart();
24 | // Set http headers
25 | header('Content-Type: image/png');
26 |
27 | $font = __DIR__ . '/font/font.ttf';
28 | $fontSize = 40;
29 |
30 | $text = (string) $graph;
31 |
32 | // Calculate the required width to hold this text
33 | $enclosingBox = imagettfbbox($fontSize, 0, $font, $text);
34 | $width = $enclosingBox[2] - $enclosingBox[0] - 10;
35 |
36 | $height = $fontSize * $graph->getSettings()->getHeight() * 2;
37 |
38 | // Create the image and define colours
39 | $im = imagecreatetruecolor($width + $fontSize, $height + $fontSize);
40 | $white = imagecolorallocate($im, 255, 255, 255);
41 | $grey = imagecolorallocate($im, 0, 0, 0);
42 |
43 | imagefilledrectangle($im, 0, 0, $width - 1, $height - 1, $grey);
44 |
45 | imagettftext($im, $fontSize, 0, $fontSize, $fontSize, $white, $font, $text);
46 |
47 | // Output and cleanup
48 | imagepng($im);
49 | imagedestroy($im);
50 |
--------------------------------------------------------------------------------
/examples/randomValues.php:
--------------------------------------------------------------------------------
1 | setHeight(30);
12 | $settings->setFPS(120);
13 | $lineGraph = new Linechart();
14 | $lineGraph->setSettings($settings);
15 |
16 | try {
17 | $lineA = [];
18 | $lineB = [];
19 | $lineC = [];
20 | $lineD = [];
21 | for ($i = 0; $i < 120; $i++) {
22 | $lineA[$i] = $lineA[$i - 1] ?? 1 + random_int(-2, 2);
23 | $lineB[$i] = $lineB[$i - 1] ?? 1 + random_int(-2, 2);
24 | $lineC[$i] = $lineC[$i - 1] ?? 1 + random_int(-2, 2);
25 | $lineD[$i] = $lineD[$i - 1] ?? 1 + random_int(-2, 2);
26 | }
27 | for ($y = 0; $y < 1500; $y++) {
28 | array_shift($lineA);
29 | $lineA[] = end($lineA) + random_int(-2, 2);
30 | array_shift($lineB);
31 | $lineB[] = end($lineB) + random_int(-2, 2);
32 | array_shift($lineC);
33 | $lineC[] = end($lineC) + random_int(-2, 2);
34 | array_shift($lineD);
35 | $lineD[] = end($lineD) + random_int(-2, 2);
36 |
37 | $lineGraph->addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD]);
38 | $lineGraph->addMarkers($lineB, [AsciiColorizer::GREEN], [AsciiColorizer::RED]);
39 | $lineGraph->addMarkers($lineC, [AsciiColorizer::CYAN, AsciiColorizer::BOLD]);
40 | $lineGraph->addMarkers($lineD, [AsciiColorizer::WHITE, AsciiColorizer::BOLD]);
41 |
42 | $lineGraph->addLine(0, [AsciiColorizer::CYAN], Linechart::FULL_LINE);
43 |
44 | $lineGraph->chart()->clearScreen()->print()->wait();
45 | $lineGraph->clearAllMarkers();
46 | }
47 | } catch (Throwable $throwable) {
48 | echo $throwable->getMessage();
49 | }
50 |
--------------------------------------------------------------------------------
/examples/example.php:
--------------------------------------------------------------------------------
1 | setColorizer(new AsciiColorizer())//Colorizer, choose between Ascii, HTML and image colorizers
15 | ->setFPS(24)//control speed of Graph::wait method
16 | ->setHeight(30)//Set fixed height of graph. Graph will scale accordingly
17 | ->setPadding(5, ' ')//Set lenght of a padding and character used
18 | ->setOffset(10)//Offset left border
19 | ->setFormat( //control how y axis labels will be printed out
20 | function ($x, Settings $settings) {
21 | $padding = $settings->getPadding();
22 | $paddingLength = strlen($padding);
23 |
24 | return substr($padding . round($x, 2), -$paddingLength);
25 | }
26 | );
27 |
28 | $lineGraph = new Linechart();
29 | $lineGraph->setSettings($settings);
30 |
31 | for ($y = 0; $y < 40000; $y++) { //Move sinusoid
32 | $lineA = [];
33 | $lineB = [];
34 | for ($i = $y; $i < $y + 120; $i++) {
35 | $lineA[] = 10 * sin($i * ((M_PI * 4) / 120)); //Draw sinusoid
36 | $lineB[] = 20 * sin($i * ((M_PI * 4) / 120)); //Draw sinusoid
37 | }
38 |
39 | $lineGraph->addMarkers(
40 | $lineA, //graph data - note that any elements with non-integer keys will be discarded
41 | [AsciiColorizer::GREEN, AsciiColorizer::BOLD], // Default color of line. Can be ommited. You can combine mutliple color codes together. If you set up HTML colorizer, you can enter css styles instead of codes. See below
42 | [AsciiColorizer::RED, AsciiColorizer::BOLD] // Color of downtrend. Can be ommited, then default color will be used instead.
43 | );
44 | //Pro-tip - combine color with bold style - it will pop-out nicely
45 |
46 | $lineGraph->addMarkers($lineB, [AsciiColorizer::CYAN]); //Add as many datasets as you want
47 |
48 | $lineGraph->addLine( //Add a guiding line - a zero line for example
49 | 0, //Alias y coordinate
50 | [AsciiColorizer::CYAN], //You can set color the same way as with series
51 | Linechart::FULL_LINE //Choose between full line and
52 | );
53 |
54 | $lineGraph->addPoint(
55 | 10,
56 | 15,
57 | [AsciiColorizer::LIGHT_BLUE],
58 | Linechart::CROSS //Point can be made more visible with crosslines. Default is Linegraph::POINT
59 | );
60 |
61 | $graph = $lineGraph->chart(); //Graph is an object with all data drawn. It can be simply echoed or we can leverage its methods for output control
62 |
63 | $graph->clearScreen(); //clears already outputed graphs
64 | $graph->print(); //Alias of echo $graph with fluent method call
65 | $graph->wait(); //Naive implementation of animation. It simply sleeps for n microseconds (defined by setFPS earlier). It does not take into account time spent on graph generation or on retrieving data
66 | $lineGraph->clearAllMarkers(); //Get rid of already processed graph data so they won't get printed again.
67 | }
68 |
--------------------------------------------------------------------------------
/src/Settings.php:
--------------------------------------------------------------------------------
1 | format = function ($x, self $settings) {
57 | $padding = $settings->getPadding();
58 | $decimals = $settings->getDecimals();
59 | $paddingLength = \strlen($padding);
60 |
61 | return substr($padding . round($x, $decimals), -$paddingLength);
62 | };
63 | }
64 |
65 | public function getPadding(): string
66 | {
67 | return $this->padding;
68 | }
69 |
70 | public function setPadding(int $length, ?string $char = null): self
71 | {
72 | if ($char === null || $char === '') {
73 | $padding = ' ';
74 | } else {
75 | $padding = $char;
76 | }
77 | $this->padding = str_pad('', $length, $padding);
78 |
79 | return $this;
80 | }
81 |
82 | public function getDecimals(): int
83 | {
84 | return $this->decimals;
85 | }
86 |
87 | public function setDecimals(int $decimals): self
88 | {
89 | $this->decimals = $decimals;
90 |
91 | return $this;
92 | }
93 |
94 | public function getHeight(): ?int
95 | {
96 | return $this->height;
97 | }
98 |
99 | public function setHeight(int $height): self
100 | {
101 | $this->height = $height;
102 |
103 | return $this;
104 | }
105 |
106 | public function getOffset(): int
107 | {
108 | return $this->offset;
109 | }
110 |
111 | public function setOffset(int $offset): self
112 | {
113 | $this->offset = $offset;
114 |
115 | return $this;
116 | }
117 |
118 | public function getFormat(): callable
119 | {
120 | return $this->format;
121 | }
122 |
123 | /**
124 | * @param callable $format =function ($x, Settings $config) {
125 | * $padding = $config->getPadding();
126 | * $paddingLength = strlen($padding);
127 | * return substr($padding . round($x, 2), -$paddingLength);
128 | * }
129 | * @return Settings
130 | */
131 | public function setFormat(callable $format): self
132 | {
133 | $this->format = $format;
134 |
135 | return $this;
136 | }
137 |
138 | public function getFps(): int
139 | {
140 | return $this->fps;
141 | }
142 |
143 | public function setFPS(int $fps): self
144 | {
145 | $this->fps = $fps;
146 |
147 | return $this;
148 | }
149 |
150 | public function getColorizer(): ColorizerInterface
151 | {
152 | return $this->colorizer ?? new AsciiColorizer();
153 | }
154 |
155 | public function setColorizer(ColorizerInterface $colorizer): self
156 | {
157 | $this->colorizer = $colorizer;
158 |
159 | return $this;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/Colorizers/AsciiColorizer.php:
--------------------------------------------------------------------------------
1 | colorExists((int) $color)) {
156 | throw new ColorException('Unknown color ' . $color . ', use constans of class Color');
157 | }
158 | }
159 |
160 | $chr27 = chr(27);
161 |
162 | return sprintf('%s[0m%s[%sm%s%s[0m', $chr27, $chr27, implode(';', $colors), $text, $chr27);
163 | }
164 |
165 | /**
166 | * @throws ReflectionException
167 | */
168 | public function colorExists(int $color): bool
169 | {
170 | if (count(self::$constants) === 0) {
171 | $oClass = new ReflectionClass(self::class);
172 | self::$constants = $oClass->getConstants();
173 | }
174 |
175 | return in_array($color, self::$constants, true);
176 | }
177 |
178 | public function getEOL(): string
179 | {
180 | return PHP_EOL;
181 | }
182 |
183 | public function processFinalText(string $text): string
184 | {
185 | return $text;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP-colored-ascii-linechart
2 |
3 | [](https://i.imgur.com/Wc7OjvO.gif)
4 |
5 | Create beautiful, versatile ASCII line-charts within Terminal, written in `PHP`.
6 |
7 | - Create multiple lines in a single chart, each with its own color,
8 | - Use points in your chart ,
9 | - Scale the chart to a desired height or let it grow and shrink freely,
10 | - Have multi-colored lines based on uptrends and downtrends,
11 | - Print charts as ASCII colored text, a HTML snippet or png image,
12 | - Use simple API that helps with animating sequence of charts.
13 |
14 | _Built upon [kroitor/asciichart](https://github.com/kroitor/asciichart)_
15 |
16 | ## Installation
17 | ```
18 | $ composer require noximo/php-colored-ascii-linechart
19 | ```
20 |
21 | ## Usage
22 |
23 | ### Simple Output:
24 |
25 | ```php
26 | $linechart = new Linechart();
27 | echo $linechart->addMarkers([1,2,3,4,5,6])->addPoint(4, 2)->chart();
28 | ```
29 | This will print a simple chart with a single point in default colors.
30 |
31 | ### Advanced Output:
32 |
33 | ```php
34 | $settings = new Settings();
35 | // Note that any setting can be ommited.
36 | $settings
37 | ->setColorizer(new AsciiColorizer()) // Colorizer, choose between Ascii, HTML and image colorizers
38 | ->setFPS(24) // Control speed of chart::wait method
39 | ->setHeight(30) // Set fixed height of chart. chart will scale accordingly. If not set, height will be calculated based on highest and lowest numbers across all sets of markers.
40 | ->setPadding(5, ' ') // Set lenght of a padding and character used
41 | ->setOffset(10) // Offset left border
42 | ->setFormat( // Control how y axis labels will be printed out
43 | function ($x, Settings $settings) {
44 | $padding = $settings->getPadding();
45 | $paddingLength = \strlen($padding);
46 |
47 | return substr($padding . round($x, 2), -$paddingLength);
48 | }
49 | );
50 |
51 | $linechart = new Linechart();
52 | $linechart->setSettings($settings);
53 |
54 | for ($y = 0; $y < 1200; $y++) { // Move sinusoid
55 | $lineA = [];
56 | $lineB = [];
57 | for ($i = $y; $i < $y + 120; $i++) {
58 | $lineA[] = 10 * sin($i * ((M_PI * 4) / 120)); // Draw sinusoid
59 | $lineB[] = 20 * sin($i * ((M_PI * 4) / 120)); // Draw sinusoid
60 | }
61 |
62 | $linechart->addMarkers(
63 | $lineA, // Chart data - note that any elements with non-integer keys will be discarded
64 | [AsciiColorizer::GREEN, AsciiColorizer::BOLD], // Default color of line. Can be ommited. You can combine mutliple color codes together. If you set up HTML colorizer, you can enter css styles instead of codes. See below
65 | [AsciiColorizer::RED, AsciiColorizer::BOLD] // Color of downtrend. Can be ommited, then default color will be used instead.
66 | ); // Pro-tip - combine color with bold style - it will pop-out nicely
67 |
68 | $linechart->addMarkers($lineB, [AsciiColorizer::CYAN]); // Add as many datasets as you want
69 |
70 | $linechart->addLine( // Add a guiding line - a zero line for example
71 | 0, // Alias y coordinate
72 | [AsciiColorizer::CYAN], // You can set color the same way as with markers
73 | Linechart::FULL_LINE // Choose between full line and dashed line
74 | );
75 |
76 | $linechart->addPoint(
77 | 10,
78 | 15,
79 | [AsciiColorizer::LIGHT_BLUE],
80 | Linechart::CROSS // Point can be made more visible with crosslines. Default is Linechart::POINT
81 | );
82 |
83 |
84 | $chart = $linechart->chart(); // Chart is an object with all data drawn. It can be simply echoed or we can leverage its methods for output control
85 |
86 | $chart->clearScreen(); // Clears already outputed charts
87 | $chart->print(); // Alias of echo $chart with fluent method call
88 | $chart->wait(); // Naive implementation of animation. It simply sleeps for n microseconds (defined by setFPS earlier). It does not take into account time spent on chart generation or on retrieving data
89 | $linechart->clearAllMarkers(); // Get rid of already processed chart data so they won't get printed again.
90 | }
91 | ```
92 |
93 | This will print out the [chart](https://i.imgur.com/Wc7OjvO.gif) shown in the [heading](https://github.com/noximo/PHP-colored-ascii-linechart#php-colored-ascii-linechart).
94 |
95 | ### HTML Output
96 |
97 | ```php
98 | $linechart = new Linechart();
99 | $settings = new Settings(); // Settings are needed in this case
100 | $settings->setColorizer(new HTMLColorizer()); // Here you need to set up HTMLColorizer
101 |
102 | $lineA = [];
103 | for ($i = 0; $i < +120; $i++) {
104 | $lineA[] = 10 * sin($i * ((M_PI * 4) / 120));
105 | }
106 |
107 | $linechart->addLine(0, ['color:white'], Linechart::FULL_LINE); // Use css styles instead of ascii color codes
108 | $linechart->addMarkers($lineA, ['color: green'], ['color: red']);
109 | $linechart->setSettings($settings);
110 |
111 | echo $linechart->chart();
112 | ```
113 |
114 | [](https://i.imgur.com/Qw78k9k.png)
115 |
116 | ### Image Output
117 | Images are a work in progress. You can look in `/examples` folder for a *beta* implementation.
118 |
119 | ## Development Pathway / To-Do
120 |
121 | - Refactoring of colorizers and printers:
122 | - single colorizer regardles of output type,
123 | - chart class rewrite so $chart->toHtml(), $chart->toPng(), $chart->toAscii() etc. exists,
124 | - Proper image support. Animated images through gifs,
125 | - Better customization (backgrounds, borders),
126 | - X axis with labels.
127 |
--------------------------------------------------------------------------------
/src/Chart.php:
--------------------------------------------------------------------------------
1 | chart = $chart;
60 | }
61 |
62 | public function __toString()
63 | {
64 | return $this->prepareChart() . $this->prepareText();
65 | }
66 |
67 | public function getSettings(): Settings
68 | {
69 | return $this->settings;
70 | }
71 |
72 | public function setSettings(Settings $settings): self
73 | {
74 | $this->settings = $settings;
75 |
76 | return $this;
77 | }
78 |
79 | public function getResults(): array
80 | {
81 | return $this->results;
82 | }
83 |
84 | public function getMin(): float
85 | {
86 | return $this->min;
87 | }
88 |
89 | public function setMin(float $min): self
90 | {
91 | $this->min = $min;
92 |
93 | return $this;
94 | }
95 |
96 | public function getMax(): float
97 | {
98 | return $this->max;
99 | }
100 |
101 | public function setMax(float $max): self
102 | {
103 | $this->max = $max;
104 |
105 | return $this;
106 | }
107 |
108 | public function setWidth(int $width): self
109 | {
110 | $this->width = $width;
111 |
112 | return $this;
113 | }
114 |
115 | public function addResult(array $result): void
116 | {
117 | $this->results[] = $result;
118 | }
119 |
120 | public function printAndwait(): self
121 | {
122 | $this->print()->wait();
123 |
124 | return $this;
125 | }
126 |
127 | public function wait(): self
128 | {
129 | usleep((int) round(1000000 / $this->settings->getFps()));
130 |
131 | return $this;
132 | }
133 |
134 | public function print(): self
135 | {
136 | $this->output($this->prepareChart());
137 | $this->output($this->prepareText());
138 |
139 | return $this;
140 | }
141 |
142 | public function toArray(): array
143 | {
144 | $return = [];
145 | foreach ($this->merge() as $row) {
146 | foreach ($row as $cell) {
147 | $return[] = $cell;
148 | }
149 | }
150 |
151 | return $return;
152 | }
153 |
154 | public function clearScreen(bool $useAlternativeMethod = false): self
155 | {
156 | if ($useAlternativeMethod) {
157 | $chr27 = chr(27);
158 | $chr91 = chr(91);
159 | $output = sprintf('%s%sH%s%sJ', $chr27, $chr91, $chr27, $chr91);
160 | } else {
161 | $output = "\033[0;0f";
162 | }
163 |
164 | $this->output($output);
165 |
166 | return $this;
167 | }
168 |
169 | public function setAlltimeMaxHeight(int $allTimeMaxHeight): self
170 | {
171 | $this->allTimeMaxHeight = $allTimeMaxHeight;
172 |
173 | return $this;
174 | }
175 |
176 | public function printText(): self
177 | {
178 | $text = $this->prepareText();
179 |
180 | $this->output($text);
181 |
182 | return $this;
183 | }
184 |
185 | private function prepareChart(): string
186 | {
187 | $return = '';
188 | foreach ($this->merge() as $row) {
189 | foreach ($row as $cell) {
190 | $return .= $cell;
191 | }
192 | $return .= $this->settings->getColorizer()->getEOL();
193 | }
194 |
195 | return $this->settings->getColorizer()->processFinalText($return);
196 | }
197 |
198 | private function prepareText(): string
199 | {
200 | $return = '';
201 | foreach ($this->chart->getText() as $row) {
202 | $line = $this->settings->getColorizer()->colorize($row[Linechart::VALUE], $row[Linechart::COLORS]);
203 | $lineLength = strlen($line);
204 | $this->longestText = $lineLength > $this->longestText ? $lineLength : $this->longestText;
205 | $line = str_pad($line, $this->longestText, ' ');
206 | $return .= $line . $this->settings->getColorizer()->getEOL();
207 | }
208 |
209 | $return = $this->settings->getColorizer()->processFinalText($return);
210 |
211 | $this->chart->clearText();
212 |
213 | return $return;
214 | }
215 |
216 | private function output(string $output): void
217 | {
218 | $fopen = fopen('php://stdout', 'wb');
219 | if (is_resource($fopen)) {
220 | fwrite($fopen, $output);
221 | }
222 | }
223 |
224 | private function merge(): array
225 | {
226 | $merged = [];
227 | foreach ($this->results as $result) {
228 | foreach ($result as $x => $row) {
229 | $merged = $this->mergeRow($merged, $row, $x);
230 | }
231 | }
232 |
233 | return $this->adjustAllTimeMaxHeight($merged, $x ?? 0);
234 | }
235 |
236 | private function mergeRow(array $merged, array $row, int $x): array
237 | {
238 | foreach ($row as $y => $cell) {
239 | $cell = (string) $cell;
240 | if ($this->shouldBeMerged($merged, $x, $y, $cell)) {
241 | $merged[$x][$y] = $cell;
242 | }
243 | }
244 |
245 | return $merged;
246 | }
247 |
248 | private function adjustAllTimeMaxHeight(array $merged, int $x): array
249 | {
250 | if ($x < $this->allTimeMaxHeight) {
251 | $width = $this->width + strlen($this->settings->getPadding());
252 | $filledArray = array_fill(0, $width, ' ');
253 | for ($i = 0, $iMax = $this->allTimeMaxHeight - $x; $i < $iMax; $i++) {
254 | $merged[] = $filledArray;
255 | }
256 | }
257 |
258 | return $merged;
259 | }
260 |
261 | private function shouldBeMerged(array $merged, int $x, int $y, string $cell): bool
262 | {
263 | return !isset($merged[$x][$y]) || ($cell !== ' ');
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/log/phpstan.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
69 | PHPStan analysis result
70 |
71 | PHPStan analysis result
72 | 2019-08-20 22:11:06
73 |
74 |
75 |
76 |
77 |
78 |
79 | | Files with errors: 2 |
80 |
81 |
82 |
83 |
84 | | C:\wamp64\www\PHPColoredAsciiLinechart\examples\image.php (9×) |
85 |
86 |
87 |
88 | |
89 | 49
90 | |
91 |
92 | Parameter #1 $im of function imagedestroy expects resource, resource|false given.
93 | - '#Parameter \#1 \$im of function imagedestroy expects resource, resource\|false given#'
94 | |
95 |
96 |
97 | |
98 | 48
99 | |
100 |
101 | Parameter #1 $im of function imagepng expects resource, resource|false given.
102 | - '#Parameter \#1 \$im of function imagepng expects resource, resource\|false given#'
103 | |
104 |
105 |
106 | |
107 | 45
108 | |
109 |
110 | Parameter #1 $im of function imagettftext expects resource, resource|false given.
111 | - '#Parameter \#1 \$im of function imagettftext expects resource, resource\|false given#'
112 | |
113 |
114 |
115 | |
116 | 43
117 | |
118 |
119 | Parameter #1 $im of function imagefilledrectangle expects resource, resource|false given.
120 | - '#Parameter \#1 \$im of function imagefilledrectangle expects resource, resource\|false given#'
121 | |
122 |
123 |
124 | |
125 | 43
126 | |
127 |
128 | Parameter #4 $x2 of function imagefilledrectangle expects int, float|int given.
129 | - '#Parameter \#4 \$x2 of function imagefilledrectangle expects int, float\|int given#'
130 | |
131 |
132 |
133 | |
134 | 41
135 | |
136 |
137 | Parameter #1 $im of function imagecolorallocate expects resource, resource|false given.
138 | - '#Parameter \#1 \$im of function imagecolorallocate expects resource, resource\|false given#'
139 | |
140 |
141 |
142 | |
143 | 40
144 | |
145 |
146 | Parameter #1 $im of function imagecolorallocate expects resource, resource|false given.
147 | - '#Parameter \#1 \$im of function imagecolorallocate expects resource, resource\|false given#'
148 | |
149 |
150 |
151 | |
152 | 39
153 | |
154 |
155 | Parameter #1 $x_size of function imagecreatetruecolor expects int, float|int given.
156 | - '#Parameter \#1 \$x_size of function imagecreatetruecolor expects int, float\|int given#'
157 | |
158 |
159 |
160 | |
161 | 36
162 | |
163 |
164 | Only numeric types are allowed in *, int|null given on the right side.
165 | - '#Only numeric types are allowed in \*, int\|null given on the right side#'
166 | |
167 |
168 |
169 |
170 | | C:\wamp64\www\PHPColoredAsciiLinechart\examples\randomValues.php (4×) |
171 |
172 |
173 |
174 | |
175 | 35
176 | |
177 |
178 | Only numeric types are allowed in +, int|false given on the left side.
179 | - '#Only numeric types are allowed in \+, int\|false given on the left side#'
180 | |
181 |
182 |
183 | |
184 | 33
185 | |
186 |
187 | Only numeric types are allowed in +, int|false given on the left side.
188 | - '#Only numeric types are allowed in \+, int\|false given on the left side#'
189 | |
190 |
191 |
192 | |
193 | 31
194 | |
195 |
196 | Only numeric types are allowed in +, int|false given on the left side.
197 | - '#Only numeric types are allowed in \+, int\|false given on the left side#'
198 | |
199 |
200 |
201 | |
202 | 29
203 | |
204 |
205 | Only numeric types are allowed in +, int|false given on the left side.
206 | - '#Only numeric types are allowed in \+, int\|false given on the left side#'
207 | |
208 |
209 |
210 |
211 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/src/Linechart.php:
--------------------------------------------------------------------------------
1 | [1,2,3.45], SELF::COLORS => [1,2,3]]];
53 | * ]
54 | */
55 | private $allmarkers = [];
56 |
57 | /** @var int */
58 | private $width;
59 |
60 | /**
61 | * @var array|null
62 | */
63 | private $currentColors;
64 |
65 | /**
66 | * @var float
67 | */
68 | private $range = 0.0;
69 |
70 | /**
71 | * @var float
72 | */
73 | private $ratio = 0.0;
74 |
75 | /**
76 | * @var float
77 | */
78 | private $min2 = 0.0;
79 |
80 | /**
81 | * @var float
82 | */
83 | private $max2 = 0.0;
84 |
85 | /**
86 | * @var int
87 | */
88 | private $rows = 1;
89 |
90 | /**
91 | * @var int
92 | */
93 | private $offset = 0;
94 |
95 | /**
96 | * @var float|null
97 | */
98 | private $adjuster;
99 |
100 | /**
101 | * @var array
102 | */
103 | private $text = [];
104 |
105 | /**
106 | * @var Settings|null
107 | */
108 | private $settings;
109 |
110 | /**
111 | * @var ColorizerInterface
112 | */
113 | private $colorizer;
114 |
115 | /**
116 | * @param int $x alias x coordinate
117 | * @param float $y alias y coordinate
118 | * @param array|null $colors
119 | * @param string|null $appearance
120 | * @return Linechart
121 | */
122 | public function addPoint(int $x, float $y, ?array $colors = null, ?string $appearance = null): self
123 | {
124 | $markers = [];
125 | $markers[0] = $y;
126 | $markers[$x] = $y;
127 | if (!in_array($appearance, [self::CROSS, self::POINT], true)) {
128 | $appearance = self::POINT;
129 | }
130 | $this->addMarkerData($markers, $colors, null, $appearance);
131 |
132 | return $this;
133 | }
134 |
135 | /**
136 | * @param array $markers
137 | * @param array|null $colors
138 | * @param array|null $colorsDown
139 | * @return Linechart
140 | */
141 | public function addMarkers(array $markers, ?array $colors = null, ?array $colorsDown = null): self
142 | {
143 | $this->addMarkerData($markers, $colors, $colorsDown);
144 |
145 | return $this;
146 | }
147 |
148 | public function chart(): Chart
149 | {
150 | $graph = new Chart($this);
151 | $graph->setSettings($this->getSettings());
152 | $this->prepareData();
153 |
154 | foreach ($this->allmarkers as $markersData) {
155 | $result = $this->getResultFromMarkersData($markersData);
156 |
157 | $this->currentColors = null;
158 | $graph->addResult($result);
159 | }
160 |
161 | $graph->setWidth($this->width);
162 |
163 | return $graph;
164 | }
165 |
166 | public function getSettings(): Settings
167 | {
168 | if ($this->settings === null) {
169 | $this->settings = new Settings();
170 | }
171 |
172 | return $this->settings;
173 | }
174 |
175 | public function setSettings(Settings $settings): self
176 | {
177 | $this->settings = $settings;
178 |
179 | return $this;
180 | }
181 |
182 | public function clearAllMarkers(): self
183 | {
184 | $this->allmarkers = [];
185 |
186 | return $this;
187 | }
188 |
189 | /**
190 | * @param string $value
191 | * @param string[] $color
192 | */
193 | public function addText(string $value, array $color): void
194 | {
195 | $this->text[] = [
196 | self::VALUE => $value,
197 | self::COLORS => $color,
198 | ];
199 | }
200 |
201 | /**
202 | * @param array $values
203 | * @param float $mainValue
204 | * @param array $colors
205 | */
206 | public function addSpread(array $values, float $mainValue, array $colors): void
207 | {
208 | foreach ($values as $value) {
209 | $colors = $colors ?? [];
210 | $appearance = $value === 1 ? self::FULL_LINE : self::DASHED_LINE;
211 | $this->addLine($value * $mainValue, $colors ?? [], $appearance);
212 | }
213 | }
214 |
215 | /**
216 | * @param float $value alias y coordinate
217 | * @param array|null $colors
218 | * @param string|null $appearance
219 | * @return Linechart
220 | */
221 | public function addLine(float $value, ?array $colors = null, ?string $appearance = null): self
222 | {
223 | $markers = [];
224 | $markers[0] = $value;
225 | if (!in_array($appearance, [self::DASHED_LINE, self::FULL_LINE], true)) {
226 | $appearance = self::DASHED_LINE;
227 | }
228 | $this->addMarkerData($markers, $colors, null, $appearance);
229 |
230 | return $this;
231 | }
232 |
233 | public function getText(): array
234 | {
235 | return $this->text;
236 | }
237 |
238 | public function clearText(): void
239 | {
240 | $this->text = [];
241 | }
242 |
243 | /**
244 | * @param array $markers
245 | * @param array|null $colors
246 | * @param array|null $colorsDown
247 | * @param string|null $point
248 | * @return Linechart
249 | */
250 | private function addMarkerData(array $markers, ?array $colors = null, ?array $colorsDown = null, ?string $point = null): self
251 | {
252 | $markersData = [
253 | self::MARKERS => $this->normalizeData($markers),
254 | self::COLORS => $colors ?? [],
255 | self::COLORS_DOWN => $colorsDown ?? $colors ?? [],
256 | self::POINT => $point,
257 | ];
258 |
259 | $this->allmarkers[] = $markersData;
260 |
261 | return $this;
262 | }
263 |
264 | private function prepareData(): void
265 | {
266 | [$min, $max, $width] = $this->findMinMax($this->allmarkers);
267 |
268 | $this->adjuster = $this->findAdjuster($min, $max);
269 | $max = $this->adjust($max);
270 | $min = $this->adjust($min);
271 |
272 | $this->range = max(1, abs($max - $min));
273 |
274 | $height = max(1, (int) ($this->getSettings()->getHeight() ?? $this->range));
275 | $this->ratio = $height / $this->range;
276 |
277 | $this->min2 = $min * $this->ratio;
278 | $this->max2 = $max * $this->ratio;
279 |
280 | $this->rows = (int) max(0, abs(round($this->max2 - $this->min2)));
281 |
282 | $this->offset = $this->getSettings()->getOffset();
283 |
284 | $this->width = $width + $this->offset;
285 | }
286 |
287 | private function getResultFromMarkersData(array $markersData): array
288 | {
289 | $markersData[self::MARKERS] = $this->adjustMarkerValues($markersData[self::MARKERS]);
290 | $this->currentColors = $this->currentColors ?? $markersData[self::COLORS];
291 |
292 | $result = $this->processBorder($this->prepareResult(), $markersData);
293 |
294 | $isPoint = in_array($markersData[self::POINT], [self::CROSS, self::POINT], true);
295 | $isLine = in_array($markersData[self::POINT], [self::DASHED_LINE, self::FULL_LINE], true);
296 |
297 | foreach ($markersData[self::MARKERS] as $x => $value) {
298 | $y0 = (int) (round($value * $this->ratio) - $this->min2);
299 |
300 | if ($this->isPresent($markersData[self::MARKERS], $x + 1)) {
301 | $result = $this->processLinearGraph($result, $markersData, $x, $y0);
302 | } elseif ($x !== 0 && $isPoint) {
303 | $result = $this->processPoint($result, $markersData, $y0, $x);
304 | } elseif ($x === 0 && $isLine) {
305 | $result = $this->processLine($result, $y0, $markersData[self::POINT]);
306 | }
307 | }
308 |
309 | return $result;
310 | }
311 |
312 | private function normalizeData(array $markers): array
313 | {
314 | $markers = array_filter($markers, '\is_int', ARRAY_FILTER_USE_KEY);
315 | ksort($markers);
316 |
317 | reset($markers);
318 | $firstKey = key($markers);
319 |
320 | $keys = [];
321 | foreach (array_keys($markers) as $key) {
322 | $keys[] = $key - $firstKey;
323 | }
324 |
325 | $combined = array_combine($keys, $markers);
326 |
327 | if ($combined === false) {
328 | return [];
329 | }
330 |
331 | return $combined;
332 | }
333 |
334 | private function findMinMax(array $allmarkers): array
335 | {
336 | $width = 0;
337 | $min = PHP_INT_MAX;
338 | $max = -PHP_INT_MAX;
339 | foreach ($allmarkers as $markers) {
340 | end($markers[self::MARKERS]);
341 | $width = (int) max($width, key($markers[self::MARKERS]));
342 |
343 | /** @var int[][] $markers */
344 | foreach ($markers[self::MARKERS] as $value) {
345 | if ($value !== null && $value !== false) {
346 | $min = min($min, $value);
347 | $max = max($max, $value);
348 | }
349 | }
350 | }
351 |
352 | return [$min, $max, $width];
353 | }
354 |
355 | private function findAdjuster(float $min, float $max): ?float
356 | {
357 | $adjuster = null;
358 | $realMin = $max - $min;
359 |
360 | if ($realMin < 1 && $realMin > 0) {
361 | $adjuster = 1 / $realMin;
362 | }
363 |
364 | return $adjuster;
365 | }
366 |
367 | private function adjust(float $number): float
368 | {
369 | if ($this->adjuster !== null) {
370 | $number *= $this->adjuster;
371 | }
372 |
373 | return $number;
374 | }
375 |
376 | private function adjustMarkerValues(array $markers): array
377 | {
378 | if ($this->adjuster === null) {
379 | return $markers;
380 | }
381 |
382 | return array_map(function ($value) {
383 | return $this->adjuster !== null ? $value * $this->adjuster : $value;
384 | }, $markers);
385 | }
386 |
387 | private function processBorder(array $result, array $markersData): array
388 | {
389 | $format = $this->getSettings()->getFormat();
390 | $y0 = (int) (round($markersData[self::MARKERS][0] * $this->ratio) - $this->min2);
391 | $y = (int) floor($this->min2);
392 | $yMax = (int) ceil($this->max2);
393 |
394 | for (; $y <= $yMax; ++$y) {
395 | $rows = $this->rows === 0 ? 1 : $this->rows;
396 | $rawLabel = $this->max2 / $this->ratio - ($y - $this->min2) * $this->range / $rows;
397 | $rawLabel = $this->deadjust($rawLabel);
398 | $label = $format($rawLabel, $this->getSettings());
399 |
400 | $border = '┤';
401 | if ($y - $this->min2 === (float) ($rows - $y0)) {
402 | $label = $this->colorize($label, $this->currentColors);
403 | $border = $this->colorize('┼', $this->currentColors);
404 | }
405 |
406 | $result[$y - $this->min2][max($this->offset - strlen($label), 0)] = $label;
407 | $result[$y - $this->min2][$this->offset - 1] = $border;
408 | }
409 |
410 | return $result;
411 | }
412 |
413 | private function prepareResult(): array
414 | {
415 | $result = [];
416 |
417 | /** @noinspection ForeachInvariantsInspection */
418 | for ($i = 0; $i <= $this->rows; $i++) {
419 | $result[$i] = array_fill(0, $this->width, ' ');
420 | }
421 |
422 | return $result;
423 | }
424 |
425 | private function isPresent(array $markers, int $x): bool
426 | {
427 | return isset($markers[$x]) && ($markers[$x] !== null && $markers[$x] !== false);
428 | }
429 |
430 | private function processLinearGraph(array $result, array $markersData, int $x, int $y): array
431 | {
432 | $y1 = (int) (round($markersData[self::MARKERS][$x + 1] * $this->ratio) - $this->min2);
433 | if ($y === $y1) {
434 | $result[$this->rows - $y][$x + $this->offset] = $this->colorize('─', $this->currentColors);
435 | } else {
436 | if ($y > $y1) {
437 | $connectA = '╰';
438 | $connectB = '╮';
439 |
440 | $this->currentColors = $markersData[self::COLORS_DOWN];
441 | } else {
442 | $connectA = '╭';
443 | $connectB = '╯';
444 |
445 | $this->currentColors = $markersData[self::COLORS];
446 | }
447 | $result[$this->rows - $y1][$x + $this->offset] = $this->colorize($connectA, $this->currentColors);
448 | $result[$this->rows - $y][$x + $this->offset] = $this->colorize($connectB, $this->currentColors);
449 |
450 | $from = min($y, $y1);
451 | $to = max($y, $y1);
452 | for ($i = $from + 1; $i < $to; $i++) {
453 | $result[$this->rows - $i][$x + $this->offset] = $this->colorize('│', $this->currentColors);
454 | }
455 | }
456 |
457 | return $result;
458 | }
459 |
460 | private function processPoint(array $result, array $markersData, int $y, int $x): array
461 | {
462 | if ($markersData[self::POINT] === self::CROSS) {
463 | for ($i = 0; $i <= $this->width - $this->offset - 2; $i++) {
464 | $result[$this->rows - $y][$i + $this->offset] = $this->colorize('╌', $this->currentColors);
465 | }
466 | for ($i = 0; $i <= $this->rows; $i++) {
467 | $result[$this->rows - $i][$x + $this->offset] = $this->colorize('╎', $this->currentColors);
468 | }
469 | }
470 |
471 | $result[$this->rows - $y][$x + $this->offset] = $this->colorize('o', $this->currentColors);
472 |
473 | return $result;
474 | }
475 |
476 | private function processLine(array $result, int $y, string $lineStyle): array
477 | {
478 | $line = '╌';
479 | if ($lineStyle === self::FULL_LINE) {
480 | $line = '─';
481 | }
482 |
483 | for ($i = 0; $i <= $this->width - $this->offset - 2; $i++) {
484 | $result[$this->rows - $y][$i + $this->offset] = $this->colorize($line, $this->currentColors);
485 | }
486 |
487 | return $result;
488 | }
489 |
490 | private function deadjust(float $number): float
491 | {
492 | if ($this->adjuster !== null) {
493 | $number /= $this->adjuster;
494 | }
495 |
496 | return $number;
497 | }
498 |
499 | private function colorize(string $label, ?array $currentColors = null): string
500 | {
501 | if ($this->colorizer === null) {
502 | $this->colorizer = $this->getSettings()->getColorizer();
503 | }
504 |
505 | return $this->colorizer->colorize($label, $currentColors);
506 | }
507 | }
508 |
--------------------------------------------------------------------------------