From c68552a0fb78834448f96533d916a078d65084f0 Mon Sep 17 00:00:00 2001 From: Sergei Solovev Date: Tue, 19 Dec 2023 21:38:10 +0300 Subject: [PATCH] Added a graph library --- .gitignore | 3 +- composer.json | 3 +- composer.lock | 73 +- system/engine/graph.php | 4 - system/library/cron/threads.php | 14 +- system/library/games/graph.php | 25 +- system/sections/servers/cs/graph.php | 4 - vendor/composer/autoload_files.php | 1 + vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 9 + vendor/composer/installed.json | 74 + vendor/composer/installed.php | 9 + .../.github/workflows/codacy-analysis.yml | 49 + vendor/szymach/c-pchart/.gitignore | 6 + vendor/szymach/c-pchart/.travis.yml | 33 + vendor/szymach/c-pchart/CHANGELOG.md | 48 + vendor/szymach/c-pchart/LICENSE | 675 + vendor/szymach/c-pchart/README.md | 185 + vendor/szymach/c-pchart/cache/.gitkeep | 1 + vendor/szymach/c-pchart/codeception.yml | 21 + vendor/szymach/c-pchart/composer.json | 52 + vendor/szymach/c-pchart/constants.php | 206 + vendor/szymach/c-pchart/coverage.sh | 7 + vendor/szymach/c-pchart/docker-compose.yml | 9 + vendor/szymach/c-pchart/phpcs.xml | 26 + .../c-pchart/resources/barcode/128B.db | 107 + .../szymach/c-pchart/resources/barcode/39.db | 44 + .../szymach/c-pchart/resources/doc/2d_pie.md | 87 + .../szymach/c-pchart/resources/doc/2d_ring.md | 72 + .../szymach/c-pchart/resources/doc/3d_pie.md | 119 + .../szymach/c-pchart/resources/doc/3d_ring.md | 78 + vendor/szymach/c-pchart/resources/doc/area.md | 72 + vendor/szymach/c-pchart/resources/doc/bar.md | 64 + .../c-pchart/resources/doc/barcode_128.md | 89 + .../c-pchart/resources/doc/barcode_39.md | 82 + .../c-pchart/resources/doc/best_fit.md | 70 + .../szymach/c-pchart/resources/doc/bubble.md | 107 + .../szymach/c-pchart/resources/doc/cache.md | 83 + .../szymach/c-pchart/resources/doc/contour.md | 92 + .../c-pchart/resources/doc/filled_spline.md | 125 + .../c-pchart/resources/doc/filled_step.md | 75 + vendor/szymach/c-pchart/resources/doc/line.md | 50 + vendor/szymach/c-pchart/resources/doc/plot.md | 67 + .../szymach/c-pchart/resources/doc/polar.md | 96 + .../c-pchart/resources/doc/progress.md | 71 + .../szymach/c-pchart/resources/doc/radar.md | 98 + .../resources/doc/scatter_best_fit.md | 108 + .../c-pchart/resources/doc/scatter_line.md | 100 + .../c-pchart/resources/doc/scatter_plot.md | 107 + .../c-pchart/resources/doc/scatter_spline.md | 108 + .../resources/doc/scatter_threshold.md | 116 + .../resources/doc/scatter_threshold_area.md | 116 + .../szymach/c-pchart/resources/doc/spline.md | 37 + .../c-pchart/resources/doc/split_path.md | 74 + .../szymach/c-pchart/resources/doc/spring.md | 62 + .../c-pchart/resources/doc/stacked_area.md | 88 + .../c-pchart/resources/doc/stacked_bar.md | 87 + vendor/szymach/c-pchart/resources/doc/step.md | 93 + .../szymach/c-pchart/resources/doc/stock.md | 78 + .../szymach/c-pchart/resources/doc/surface.md | 81 + vendor/szymach/c-pchart/resources/doc/zone.md | 71 + .../c-pchart/resources/fonts/Bedizen.ttf | Bin 0 -> 42912 bytes .../c-pchart/resources/fonts/Forgotte.ttf | Bin 0 -> 42148 bytes .../c-pchart/resources/fonts/GeosansLight.ttf | Bin 0 -> 60072 bytes .../c-pchart/resources/fonts/MankSans.ttf | Bin 0 -> 58492 bytes .../c-pchart/resources/fonts/Silkscreen.ttf | Bin 0 -> 16172 bytes .../c-pchart/resources/fonts/advent_light.ttf | Bin 0 -> 45768 bytes .../c-pchart/resources/fonts/calibri.ttf | Bin 0 -> 811052 bytes .../c-pchart/resources/fonts/pf_arma_five.ttf | Bin 0 -> 21936 bytes .../c-pchart/resources/fonts/verdana.ttf | Bin 0 -> 189144 bytes .../c-pchart/resources/palettes/autumn.color | 6 + .../c-pchart/resources/palettes/blind.color | 6 + .../c-pchart/resources/palettes/evening.color | 6 + .../c-pchart/resources/palettes/kitchen.color | 6 + .../c-pchart/resources/palettes/light.color | 7 + .../c-pchart/resources/palettes/navy.color | 6 + .../c-pchart/resources/palettes/shade.color | 6 + .../c-pchart/resources/palettes/spring.color | 6 + .../c-pchart/resources/palettes/summer.color | 6 + .../c-pchart/src/Barcode/Barcode128.php | 274 + .../c-pchart/src/Barcode/Barcode39.php | 296 + vendor/szymach/c-pchart/src/BaseDraw.php | 1728 +++ vendor/szymach/c-pchart/src/Cache.php | 392 + vendor/szymach/c-pchart/src/Chart/Bubble.php | 532 + .../szymach/c-pchart/src/Chart/Indicator.php | 377 + vendor/szymach/c-pchart/src/Chart/Pie.php | 2320 ++++ vendor/szymach/c-pchart/src/Chart/Radar.php | 1074 ++ vendor/szymach/c-pchart/src/Chart/Scatter.php | 2255 ++++ vendor/szymach/c-pchart/src/Chart/Split.php | 180 + vendor/szymach/c-pchart/src/Chart/Spring.php | 1190 ++ vendor/szymach/c-pchart/src/Chart/Stock.php | 453 + vendor/szymach/c-pchart/src/Chart/Surface.php | 414 + vendor/szymach/c-pchart/src/Data.php | 1318 ++ vendor/szymach/c-pchart/src/Draw.php | 10363 ++++++++++++++++ vendor/szymach/c-pchart/src/Image.php | 748 ++ vendor/szymach/c-pchart/tests/_data/.gitkeep | 0 .../szymach/c-pchart/tests/_data/accept.png | Bin 0 -> 781 bytes .../c-pchart/tests/_data/test_palette.txt | 11 + .../szymach/c-pchart/tests/_output/.gitignore | 2 + .../c-pchart/tests/_support/Helper/Unit.php | 42 + .../c-pchart/tests/_support/UnitTester.php | 33 + .../tests/_support/_generated/.gitignore | 2 + vendor/szymach/c-pchart/tests/unit.suite.yml | 6 + .../szymach/c-pchart/tests/unit/AreaTest.php | 80 + .../c-pchart/tests/unit/BarCodeTest.php | 138 + .../szymach/c-pchart/tests/unit/BarTest.php | 110 + .../c-pchart/tests/unit/BestFitTest.php | 64 + .../c-pchart/tests/unit/BubbleTest.php | 119 + .../szymach/c-pchart/tests/unit/CacheTest.php | 134 + .../c-pchart/tests/unit/FilledSplineTest.php | 110 + .../c-pchart/tests/unit/FilledStepTest.php | 116 + .../szymach/c-pchart/tests/unit/LineTest.php | 76 + .../szymach/c-pchart/tests/unit/PieTest.php | 343 + .../szymach/c-pchart/tests/unit/PlotTest.php | 65 + .../szymach/c-pchart/tests/unit/PolarTest.php | 113 + .../c-pchart/tests/unit/ProgressTest.php | 96 + .../szymach/c-pchart/tests/unit/RadarTest.php | 107 + .../c-pchart/tests/unit/RegressionTest.php | 69 + .../c-pchart/tests/unit/ResourceTest.php | 73 + .../c-pchart/tests/unit/ScatterTest.php | 327 + .../c-pchart/tests/unit/SplineTest.php | 45 + .../c-pchart/tests/unit/SplitPathTest.php | 90 + .../c-pchart/tests/unit/SpringTest.php | 114 + .../c-pchart/tests/unit/StackedAreaTest.php | 77 + .../c-pchart/tests/unit/StackedBarTest.php | 85 + .../szymach/c-pchart/tests/unit/StepTest.php | 112 + .../szymach/c-pchart/tests/unit/StockTest.php | 91 + .../c-pchart/tests/unit/SurfaceTest.php | 171 + .../szymach/c-pchart/tests/unit/ZoneTest.php | 64 + 129 files changed, 31640 insertions(+), 26 deletions(-) create mode 100644 vendor/szymach/c-pchart/.github/workflows/codacy-analysis.yml create mode 100644 vendor/szymach/c-pchart/.gitignore create mode 100644 vendor/szymach/c-pchart/.travis.yml create mode 100644 vendor/szymach/c-pchart/CHANGELOG.md create mode 100644 vendor/szymach/c-pchart/LICENSE create mode 100644 vendor/szymach/c-pchart/README.md create mode 100644 vendor/szymach/c-pchart/cache/.gitkeep create mode 100644 vendor/szymach/c-pchart/codeception.yml create mode 100644 vendor/szymach/c-pchart/composer.json create mode 100644 vendor/szymach/c-pchart/constants.php create mode 100644 vendor/szymach/c-pchart/coverage.sh create mode 100644 vendor/szymach/c-pchart/docker-compose.yml create mode 100644 vendor/szymach/c-pchart/phpcs.xml create mode 100644 vendor/szymach/c-pchart/resources/barcode/128B.db create mode 100644 vendor/szymach/c-pchart/resources/barcode/39.db create mode 100644 vendor/szymach/c-pchart/resources/doc/2d_pie.md create mode 100644 vendor/szymach/c-pchart/resources/doc/2d_ring.md create mode 100644 vendor/szymach/c-pchart/resources/doc/3d_pie.md create mode 100644 vendor/szymach/c-pchart/resources/doc/3d_ring.md create mode 100644 vendor/szymach/c-pchart/resources/doc/area.md create mode 100644 vendor/szymach/c-pchart/resources/doc/bar.md create mode 100644 vendor/szymach/c-pchart/resources/doc/barcode_128.md create mode 100644 vendor/szymach/c-pchart/resources/doc/barcode_39.md create mode 100644 vendor/szymach/c-pchart/resources/doc/best_fit.md create mode 100644 vendor/szymach/c-pchart/resources/doc/bubble.md create mode 100644 vendor/szymach/c-pchart/resources/doc/cache.md create mode 100644 vendor/szymach/c-pchart/resources/doc/contour.md create mode 100644 vendor/szymach/c-pchart/resources/doc/filled_spline.md create mode 100644 vendor/szymach/c-pchart/resources/doc/filled_step.md create mode 100644 vendor/szymach/c-pchart/resources/doc/line.md create mode 100644 vendor/szymach/c-pchart/resources/doc/plot.md create mode 100644 vendor/szymach/c-pchart/resources/doc/polar.md create mode 100644 vendor/szymach/c-pchart/resources/doc/progress.md create mode 100644 vendor/szymach/c-pchart/resources/doc/radar.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_best_fit.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_line.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_plot.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_spline.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_threshold.md create mode 100644 vendor/szymach/c-pchart/resources/doc/scatter_threshold_area.md create mode 100644 vendor/szymach/c-pchart/resources/doc/spline.md create mode 100644 vendor/szymach/c-pchart/resources/doc/split_path.md create mode 100644 vendor/szymach/c-pchart/resources/doc/spring.md create mode 100644 vendor/szymach/c-pchart/resources/doc/stacked_area.md create mode 100644 vendor/szymach/c-pchart/resources/doc/stacked_bar.md create mode 100644 vendor/szymach/c-pchart/resources/doc/step.md create mode 100644 vendor/szymach/c-pchart/resources/doc/stock.md create mode 100644 vendor/szymach/c-pchart/resources/doc/surface.md create mode 100644 vendor/szymach/c-pchart/resources/doc/zone.md create mode 100644 vendor/szymach/c-pchart/resources/fonts/Bedizen.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/Forgotte.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/GeosansLight.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/MankSans.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/Silkscreen.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/advent_light.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/calibri.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/pf_arma_five.ttf create mode 100644 vendor/szymach/c-pchart/resources/fonts/verdana.ttf create mode 100644 vendor/szymach/c-pchart/resources/palettes/autumn.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/blind.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/evening.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/kitchen.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/light.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/navy.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/shade.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/spring.color create mode 100644 vendor/szymach/c-pchart/resources/palettes/summer.color create mode 100644 vendor/szymach/c-pchart/src/Barcode/Barcode128.php create mode 100644 vendor/szymach/c-pchart/src/Barcode/Barcode39.php create mode 100644 vendor/szymach/c-pchart/src/BaseDraw.php create mode 100644 vendor/szymach/c-pchart/src/Cache.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Bubble.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Indicator.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Pie.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Radar.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Scatter.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Split.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Spring.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Stock.php create mode 100644 vendor/szymach/c-pchart/src/Chart/Surface.php create mode 100644 vendor/szymach/c-pchart/src/Data.php create mode 100644 vendor/szymach/c-pchart/src/Draw.php create mode 100644 vendor/szymach/c-pchart/src/Image.php create mode 100644 vendor/szymach/c-pchart/tests/_data/.gitkeep create mode 100644 vendor/szymach/c-pchart/tests/_data/accept.png create mode 100644 vendor/szymach/c-pchart/tests/_data/test_palette.txt create mode 100644 vendor/szymach/c-pchart/tests/_output/.gitignore create mode 100644 vendor/szymach/c-pchart/tests/_support/Helper/Unit.php create mode 100644 vendor/szymach/c-pchart/tests/_support/UnitTester.php create mode 100644 vendor/szymach/c-pchart/tests/_support/_generated/.gitignore create mode 100644 vendor/szymach/c-pchart/tests/unit.suite.yml create mode 100644 vendor/szymach/c-pchart/tests/unit/AreaTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/BarCodeTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/BarTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/BestFitTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/BubbleTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/CacheTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/FilledSplineTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/FilledStepTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/LineTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/PieTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/PlotTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/PolarTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/ProgressTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/RadarTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/RegressionTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/ResourceTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/ScatterTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/SplineTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/SplitPathTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/SpringTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/StackedAreaTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/StackedBarTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/StepTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/StockTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/SurfaceTest.php create mode 100644 vendor/szymach/c-pchart/tests/unit/ZoneTest.php diff --git a/.gitignore b/.gitignore index d48c759..a81f8f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -.vscode \ No newline at end of file +.vscode +composer.phar \ No newline at end of file diff --git a/composer.json b/composer.json index bc5d65d..fae15eb 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,7 @@ "symfony/polyfill": "1.28.0", "filp/whoops": "2.15.4", "monolog/monolog": "2.9.2", - "xpaw/php-source-query-class": "2.1.0" + "xpaw/php-source-query-class": "2.1.0", + "szymach/c-pchart": "3.0.17" } } \ No newline at end of file diff --git a/composer.lock b/composer.lock index 942c121..3f9353a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "567327add4f8b463f2486e21cc511bea", + "content-hash": "9fe541d20000bad8bcc7a6f093bd89a1", "packages": [ { "name": "filp/whoops", @@ -343,6 +343,77 @@ ], "time": "2023-08-25T17:27:34+00:00" }, + { + "name": "szymach/c-pchart", + "version": "v3.0.17", + "source": { + "type": "git", + "url": "https://github.com/szymach/c-pchart.git", + "reference": "c022568da78f73bb7acf7db7a974934a434d47d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szymach/c-pchart/zipball/c022568da78f73bb7acf7db7a974934a434d47d7", + "reference": "c022568da78f73bb7acf7db7a974934a434d47d7", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": "^5.4|^7.0|^8.0" + }, + "require-dev": { + "codeception/codeception": "^4.1.22", + "codeception/module-asserts": "^1.3", + "codeception/module-filesystem": "^1.0", + "phpunit/phpunit": "^5.7|^9.5", + "squizlabs/php_codesniffer": "^3.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev", + "2.0": "2.0.x-dev" + } + }, + "autoload": { + "files": [ + "constants.php" + ], + "psr-4": { + "CpChart\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-only" + ], + "authors": [ + { + "name": "Jean-Damien Pogolotti", + "homepage": "http://www.pchart.net", + "role": "Creator of the original pChart library" + }, + { + "name": "Piotr Szymaszek", + "homepage": "https://github.com/szymach", + "role": "Developer of the CpChart wrapper package" + } + ], + "description": "Port of \"pChart\" library into PHP 5+", + "homepage": "https://github.com/szymach/c-pchart", + "keywords": [ + "CpChart", + "c-pChart", + "charts", + "pchart", + "statistics" + ], + "support": { + "issues": "https://github.com/szymach/c-pchart/issues", + "source": "https://github.com/szymach/c-pchart/tree/v3.0.17" + }, + "time": "2023-05-27T11:12:02+00:00" + }, { "name": "xpaw/php-source-query-class", "version": "2.1.0", diff --git a/system/engine/graph.php b/system/engine/graph.php index 4eb7491..8de54f9 100644 --- a/system/engine/graph.php +++ b/system/engine/graph.php @@ -14,10 +14,6 @@ if (!$sql->num()) $graph = $sql->get(); -include(LIB . 'games/graph/pData.php'); -include(LIB . 'games/graph/pDraw.php'); -include(LIB . 'games/graph/pImage.php'); - if (isset($url['type'])) { include(DATA . 'graph.php'); diff --git a/system/library/cron/threads.php b/system/library/cron/threads.php index 6bbf0e3..904240b 100644 --- a/system/library/cron/threads.php +++ b/system/library/cron/threads.php @@ -15,7 +15,7 @@ class threads extends cron return NULL; while ($unit = $sql->get()) - $aUnit[$unit['id']] = ''; + $aUnit[$unit['id']] = []; $sql->query('SELECT `id` FROM `servers` LIMIT 1'); @@ -26,14 +26,19 @@ class threads extends cron $all = $sql->num(); - while ($server = $sql->get()) - $aUnit[$server['unit']][$server['game']] = $server['id'] . ' '; + while($server = $sql->get()) { + $aUnit[$server['unit']][$server['game']] ??= []; + $aUnit[$server['unit']][$server['game']][] = $server['id']; + } if ($argv[3] == 'scan_servers_route') cron::$seping = 50; foreach ($aUnit as $unit => $aGame) { foreach ($aGame as $game => $servers) { + if(is_array($servers)) { + $servers = implode(' ', $servers); + } $aData = explode(' ', $servers); $num = count($aData) - 1; @@ -49,9 +54,10 @@ class threads extends cron foreach ($threads as $thread) { foreach ($thread as $screen => $servers) - $cmd .= 'sudo -u www-data screen -dmS scan_' . (sys::first(explode(' ', $servers))) . '_' . $screen . ' taskset -c ' . $cfg['cron_taskset'] . ' sh -c \"cd /var/enginegp; php cron.php ' . $cfg['cron_key'] . ' ' . $argv[3] . ' ' . $servers . '\"; sleep 1;'; + $cmd .= 'sudo -u www-data screen -dmS scan_' . (sys::first(explode(' ', $servers))) . '_' . $screen . ' taskset -c ' . $cfg['cron_taskset'] . ' sh -c \"cd /var/www/enginegp; php7.4 cron.php ' . $cfg['cron_key'] . ' ' . $argv[3] . ' ' . $servers . '\"; sleep 1;'; } + $start_point = $_SERVER['REQUEST_TIME']; exec('screen -dmS threads_' . date('His', $start_point) . ' sh -c "' . $cmd . '"'); return NULL; diff --git a/system/library/games/graph.php b/system/library/games/graph.php index 6158a90..818ad8f 100644 --- a/system/library/games/graph.php +++ b/system/library/games/graph.php @@ -2,6 +2,9 @@ if (!DEFINED('EGP')) exit(header('Refresh: 0; URL=http://' . $_SERVER['SERVER_NAME'] . '/404')); +use CpChart\Data; +use CpChart\Image; + class graph { public static function full($server, $slots, $key, $time) @@ -15,7 +18,7 @@ class graph $aRAM = $aData['ram']; $aHDD = $aData['hdd']; - $MyData = new pData(); + $MyData = new Data(); // Онлайн $MyData->addPoints($aOnline, 'ONLINE'); @@ -51,7 +54,7 @@ class graph $MyData->setPalette('RAM', array('R' => 26, 'G' => 150, 'B' => 38)); $MyData->setPalette('HDD', array('R' => 205, 'G' => 196, 'B' => 37)); - $myPicture = new pImage(896, 220, $MyData); + $myPicture = new Image(896, 220, $MyData); $myPicture->drawFilledRectangle(0, 0, 896, 220, array('R' => 255, 'G' => 255, 'B' => 255)); @@ -60,17 +63,17 @@ class graph $myPicture->setFontProperties(array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)); $myPicture->setGraphArea(40, 20, 616, 190); $myPicture->drawFilledRectangle(40, 20, 616, 190, array('R' => 240, 'G' => 242, 'B' => 242, 'Alpha' => 100)); - $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'GridR' => 76, 'GridG' => 109, 'GridB' => 120, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($slots), 1 => array(25, 50, 75, 100)), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $slots), 1 => array('Min' => 0, 'Max' => 100)))); + $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'GridR' => 76, 'GridG' => 109, 'GridB' => 120, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'ManualScale' => array(0 => array('Min' => 0, 'Max' => 32), 1 => array('Min' => 0, 'Max' => 100)))); $myPicture->drawText(676, 34, 'Средний онлайн: ' . graph::average($aOnline), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->drawText(676, 54, 'Средняя нагрузка (CPU): ' . graph::average($aCPU) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->drawText(676, 74, 'Средняя нагрузка (RAM): ' . graph::average($aRAM) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); - $myPicture->drawText(676, 94, 'Средняя нагрузка (HDD): ' . graph::average($aHDD) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); + $myPicture->drawText(676, 94, 'Средняя нагрузка (HDD): ' . graph::average($aHDD), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->drawText(676, 129, 'Максимальный онлайн: ' . max($aOnline), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->drawText(676, 153, 'Максимальная нагрузка (CPU): ' . max($aCPU) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->drawText(676, 173, 'Максимальная нагрузка (RAM): ' . max($aRAM) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); - $myPicture->drawText(676, 193, 'Максимальная нагрузка (HDD): ' . max($aHDD) . '%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); + $myPicture->drawText(676, 193, 'Максимальная нагрузка (HDD): ' . max($aHDD), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB . 'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT)); $myPicture->setFontProperties(array('FontName' => LIB . 'games/graph/fonts/tahoma.ttf', 'FontSize' => 7)); $myPicture->drawSplineChart(); @@ -89,7 +92,7 @@ class graph { global $cfg, $aGname; - $MyData = new pData(); + $MyData = new Data(); // Значения $MyData->addPoints($aPoints, 'ONLINE'); @@ -101,7 +104,7 @@ class graph $MyData->setPalette('ONLINE', $aStyle[$style]['line']); // Размер баннера - $myPicture = new pImage(160, 248, $MyData); + $myPicture = new Image(160, 248, $MyData); // Цвет фона $myPicture->drawFilledRectangle(0, 0, 160, 248, $aStyle[$style]['fon']); @@ -119,7 +122,7 @@ class graph $myPicture->drawFilledRectangle(35, 160, 150, 210, $aStyle[$style]['graph']); // График - $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($server['slots_start'])), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start'])))); + $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array($server['slots_start']), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start'])))); // Название игрового сервера $myPicture->drawFilledRectangle(0, 0, 18, 248, $aStyle[$style]['leftbox']); @@ -166,7 +169,7 @@ class graph { global $cfg, $aGname; - $MyData = new pData(); + $MyData = new Data(); // Значения $MyData->addPoints($aPoints, 'ONLINE'); @@ -178,7 +181,7 @@ class graph $MyData->setPalette('ONLINE', $aStyle[$style]['line']); // Размер баннера - $myPicture = new pImage(560, 95, $MyData); + $myPicture = new Image(560, 95, $MyData); // Цвет фона $myPicture->drawFilledRectangle(0, 0, 560, 95, $aStyle[$style]['fon']); @@ -204,7 +207,7 @@ class graph $myPicture->drawFilledRectangle(430, 5, 554, 60, $aStyle[$style]['graph']); // График - $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($server['slots_start'])), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start'])))); + $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array($server['slots_start']), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start'])))); // Адрес игрового сервера $myPicture->drawFilledRectangle(5, 36, 210, 49, $aStyle[$style]['box']); diff --git a/system/sections/servers/cs/graph.php b/system/sections/servers/cs/graph.php index b153b98..b57a4b4 100644 --- a/system/sections/servers/cs/graph.php +++ b/system/sections/servers/cs/graph.php @@ -19,10 +19,6 @@ if ($go) { exit(file_get_contents(TEMP . (md5($graph['key'] . 'full_' . $time)) . '.png')); } - include(LIB . 'games/graph/pData.php'); - include(LIB . 'games/graph/pDraw.php'); - include(LIB . 'games/graph/pImage.php'); - include(LIB . 'games/graph.php'); graph::full($id, $server['slots_start'], $graph['key'], $time); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index a1a0fd8..43d9b9d 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -17,4 +17,5 @@ return array( 'a4e34e0535907b5c234b9abc547237ca' => $vendorDir . '/symfony/polyfill/src/Intl/MessageFormatter/bootstrap.php', '299b3c040b39cb03c6eceb9bb272ad1d' => $vendorDir . '/symfony/polyfill/src/Intl/Normalizer/bootstrap.php', 'e59f725579f9974327c76777296d6dcc' => $vendorDir . '/symfony/polyfill/src/Mbstring/bootstrap.php', + '7bb4f001eb5212bde073bf47a4bbedad' => $vendorDir . '/szymach/c-pchart/constants.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index f46a8a1..d887cfa 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -11,4 +11,5 @@ return array( 'Symfony\\Polyfill\\' => array($vendorDir . '/symfony/polyfill/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), + 'CpChart\\' => array($vendorDir . '/szymach/c-pchart/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index d8d208e..5ad97d6 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -18,6 +18,7 @@ class ComposerStaticInit76468c2eb232fdf4a8e3502b319270a4 'a4e34e0535907b5c234b9abc547237ca' => __DIR__ . '/..' . '/symfony/polyfill/src/Intl/MessageFormatter/bootstrap.php', '299b3c040b39cb03c6eceb9bb272ad1d' => __DIR__ . '/..' . '/symfony/polyfill/src/Intl/Normalizer/bootstrap.php', 'e59f725579f9974327c76777296d6dcc' => __DIR__ . '/..' . '/symfony/polyfill/src/Mbstring/bootstrap.php', + '7bb4f001eb5212bde073bf47a4bbedad' => __DIR__ . '/..' . '/szymach/c-pchart/constants.php', ); public static $prefixLengthsPsr4 = array ( @@ -41,6 +42,10 @@ class ComposerStaticInit76468c2eb232fdf4a8e3502b319270a4 array ( 'Monolog\\' => 8, ), + 'C' => + array ( + 'CpChart\\' => 8, + ), ); public static $prefixDirsPsr4 = array ( @@ -64,6 +69,10 @@ class ComposerStaticInit76468c2eb232fdf4a8e3502b319270a4 array ( 0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog', ), + 'CpChart\\' => + array ( + 0 => __DIR__ . '/..' . '/szymach/c-pchart/src', + ), ); public static $classMap = array ( diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index c8f34f1..6d6c725 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -349,6 +349,80 @@ ], "install-path": "../symfony/polyfill" }, + { + "name": "szymach/c-pchart", + "version": "v3.0.17", + "version_normalized": "3.0.17.0", + "source": { + "type": "git", + "url": "https://github.com/szymach/c-pchart.git", + "reference": "c022568da78f73bb7acf7db7a974934a434d47d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szymach/c-pchart/zipball/c022568da78f73bb7acf7db7a974934a434d47d7", + "reference": "c022568da78f73bb7acf7db7a974934a434d47d7", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": "^5.4|^7.0|^8.0" + }, + "require-dev": { + "codeception/codeception": "^4.1.22", + "codeception/module-asserts": "^1.3", + "codeception/module-filesystem": "^1.0", + "phpunit/phpunit": "^5.7|^9.5", + "squizlabs/php_codesniffer": "^3.4" + }, + "time": "2023-05-27T11:12:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev", + "2.0": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "constants.php" + ], + "psr-4": { + "CpChart\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-only" + ], + "authors": [ + { + "name": "Jean-Damien Pogolotti", + "homepage": "http://www.pchart.net", + "role": "Creator of the original pChart library" + }, + { + "name": "Piotr Szymaszek", + "homepage": "https://github.com/szymach", + "role": "Developer of the CpChart wrapper package" + } + ], + "description": "Port of \"pChart\" library into PHP 5+", + "homepage": "https://github.com/szymach/c-pchart", + "keywords": [ + "CpChart", + "c-pChart", + "charts", + "pchart", + "statistics" + ], + "support": { + "issues": "https://github.com/szymach/c-pchart/issues", + "source": "https://github.com/szymach/c-pchart/tree/v3.0.17" + }, + "install-path": "../szymach/c-pchart" + }, { "name": "xpaw/php-source-query-class", "version": "2.1.0", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 1f24d27..c7688ad 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -175,6 +175,15 @@ 0 => 'v1.28.0', ), ), + 'szymach/c-pchart' => array( + 'pretty_version' => 'v3.0.17', + 'version' => '3.0.17.0', + 'reference' => 'c022568da78f73bb7acf7db7a974934a434d47d7', + 'type' => 'library', + 'install_path' => __DIR__ . '/../szymach/c-pchart', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'xpaw/php-source-query-class' => array( 'pretty_version' => '2.1.0', 'version' => '2.1.0.0', diff --git a/vendor/szymach/c-pchart/.github/workflows/codacy-analysis.yml b/vendor/szymach/c-pchart/.github/workflows/codacy-analysis.yml new file mode 100644 index 0000000..fda6516 --- /dev/null +++ b/vendor/szymach/c-pchart/.github/workflows/codacy-analysis.yml @@ -0,0 +1,49 @@ +# This workflow checks out code, performs a Codacy security scan +# and integrates the results with the +# GitHub Advanced Security code scanning feature. For more information on +# the Codacy security scan action usage and parameters, see +# https://github.com/codacy/codacy-analysis-cli-action. +# For more information on Codacy Analysis CLI in general, see +# https://github.com/codacy/codacy-analysis-cli. + +name: Codacy Security Scan + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '45 3 * * 0' + +jobs: + codacy-security-scan: + name: Codacy Security Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout code + uses: actions/checkout@v2 + + # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@1.1.0 + with: + # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository + # You can also omit the token and run the tools that support default configurations + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + # Adjust severity of non-security issues + gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will handover control about PR rejection to the GitHub side + max-allowed-issues: 2147483647 + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/vendor/szymach/c-pchart/.gitignore b/vendor/szymach/c-pchart/.gitignore new file mode 100644 index 0000000..d0e1f7c --- /dev/null +++ b/vendor/szymach/c-pchart/.gitignore @@ -0,0 +1,6 @@ +.phpcs-cache +composer.phar +composer.lock +cache/* +!cache/.gitkeep +vendor diff --git a/vendor/szymach/c-pchart/.travis.yml b/vendor/szymach/c-pchart/.travis.yml new file mode 100644 index 0000000..bca4a57 --- /dev/null +++ b/vendor/szymach/c-pchart/.travis.yml @@ -0,0 +1,33 @@ +language: php + +cache: + directories: + - vendor + +matrix: + include: + - php: 5.6 + env: + - COMPOSER_FLAGS='--prefer-lowest' + - php: 7.0 + - php: 7.1 + - php: 7.2 + - php: 7.3 + - php: 7.4 + - php: 8.0 + - php: 8.1 + env: + - COVERAGE='--coverage --coverage-xml' + XDEBUG_MODE=coverage + +before_install: + - if [[ ! $COVERAGE ]]; then phpenv config-rm xdebug.ini; fi; + +before_script: composer update -n $COMPOSER_FLAGS + +script: + - vendor/bin/phpcs + - vendor/bin/codecept run unit $COVERAGE + +after_script: + - if [[ $COVERAGE ]]; then ./coverage.sh; fi; diff --git a/vendor/szymach/c-pchart/CHANGELOG.md b/vendor/szymach/c-pchart/CHANGELOG.md new file mode 100644 index 0000000..a659c00 --- /dev/null +++ b/vendor/szymach/c-pchart/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +## 1.x +1.0 Stable version with basic functionality. + +1.1 Added factory service. + +1.1.1 Changed chart loading via factory a bit (see class annotations). + +1.1.2 Updated service class with Exception handling regarding missing / wrong class name. + +1.1.3 The file with classes' constants is now loaded via Composer (thanks to ThaDafinser). + +1.1.4 Fixed code-breaking typ (thanks to subtronic). + +1.1.5 Added an option to hide the X axis or only it's values (thanks to julien-gm). + +1.1.6 Added support for closures in formatting scale (thanks to funkjedi) + +## 2.x +2.0 Updated all classes to PSR-2 standard, added typehinting where possible, updated + annotations in methods to be as accurate as possible. Added Behat testing and + restructed the namespaces into more sensible structure. + +2.0.1 Documentation updates. + +2.0.2 Changed license to GPL-3.0. + +2.0.3 Bubble chart fix (thanks to rage28). + +2.0.4 PHP 7.1 initial support, lowered minimal PHP version to 5.4. + +2.0.5 CS fixes, removed old MIT license file. + +2.0.6 A fix for PHP 7.1 (thanks to dehrk). + +2.0.7 A fix for computing color alpha (thanks to dehrk). + +2.0.8 Covered most basic functionality with tests, added a lot of documentation + and a PHP 7.1 fix. Deprecated the `\CpChart\Factory\Factory` class. + +## 3.x + +3.0 Deleted the `\CpChart\Factory\Factory` class and everything related to it. + Moved drawing and cache classes outside of `Chart` namespace. + Moved barcode to a `Barcode` namespace. + Moved `cache` and `resources` directories to library root. + Renamed `resources\data` to `resources\barcode`. diff --git a/vendor/szymach/c-pchart/LICENSE b/vendor/szymach/c-pchart/LICENSE new file mode 100644 index 0000000..10926e8 --- /dev/null +++ b/vendor/szymach/c-pchart/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/vendor/szymach/c-pchart/README.md b/vendor/szymach/c-pchart/README.md new file mode 100644 index 0000000..52c1160 --- /dev/null +++ b/vendor/szymach/c-pchart/README.md @@ -0,0 +1,185 @@ +Table of contents: +================== +* [Support](#support) +* [Build status](#build-status) +* [Code quality](#code-quality) +* [About](#about) +* [License](#license) +* [Contributing](#contributing) +* [Installation](#installation-via-composer) +* [Usage](#usage) + - [Charts created through Image class](#charts-created-through-image-class) + - [Standalone charts](#standalone-charts) + - [Barcodes](#barcodes) + - [Cache](#cache) + - [Fonts and palletes](#fonts-and-palletes) +* [Changelog](#changelog) +* [References](#references) +* [Links](#links) + +Support: +======== + +This project is supported in a basic manner and no new features will be introduced. +Issues and pull requests will be reviewed and resolved if need be, so feel free +to post them. + +Build status: +============= +- [![Build Status](https://app.travis-ci.com/szymach/c-pchart.svg?branch=master)](https://app.travis-ci.com/szymach/c-pchart) master +- [![Build Status](https://app.travis-ci.com/szymach/c-pchart.svg?branch=3.0)](https://app.travis-ci.com/szymach/c-pchart) 3.0 +- [![Build Status](https://app.travis-ci.com/szymach/c-pchart.svg?branch=2.0)](https://app.travis-ci.com/szymach/c-pchart) 2.0 + +About: +====== + +This library is a port of the excellent pChart statistics library created by Jean-Damien Pogolotti, +and aims to allow the usage of it in modern applications. This was done through +applying PSR standards to code, introducing namespaces and typehints, along with +some basic annotations to methods. + +This is the `3.x` version, which removes the factory service and reorganizes the +file structure a bit. It does not introduce any new features, but the changes are +not compatibile with the `2.x` branch. BC compatibility with the original library +is mostly retained, however you can still use the `1.x` version if you cannot risk +any of these. + +What was done: + +- Support for PHP versions from 5.4 to 8.1. + +- Made a full port of the library's functionality. I have touched very little of +the actual logic, so most code from the original library should work. + +- Defined and added namespaces to all classes. + +- Replaced all `exit()` / `die()` commands with `throw` statements. + +- Refactored the code to meet PSR-2 standard and added annotations (as best as I could figure them out) +to methods Also, typehinting was added to methods where possible, so some backwards compatibility breaks +may occur if you did some weird things. + +- Moved all constants to a [single file](constants.php). It is loaded automatically +through Composer, so no need for manual action. + +License: +======== + +It was previously stated that this package uses the [MIT](https://opensource.org/licenses/MIT) license, +which did not meet the requirements set by the original author. It is now under the +[GNU GPL v3](http://www.gnu.org/licenses/gpl-3.0.html) license, so if you wish to +use it in a commercial project, you need to pay an [appropriate fee](http://www.pchart.net/license). + +Contributing: +============= + +All in all, this is a legacy library ported over from PHP 4, so the code is neither +beautiful nor easy to understand. I did my best to modernize and cover it with +some basic tests, but there is much more that could be done. If you are willing and +have time to fix or improve anything, feel free to post a PR or issue. + +Installation (via Composer): +============================ + +For composer installation, add: + +```json +"require": { + "szymach/c-pchart": "^3.0" +}, +``` + +to your composer.json file and update your dependencies. Or you can run: + +```sh +$ composer require szymach/c-pchart +``` + +in your project's root directory. + +Usage: +====== + +Your best source to understanding how to use the library is still the [official wiki](http://wiki.pchart.net/). +However, I have ported at least one example for each chart into Markdown files, +so you can compare each version and figure out how to use the current implementation. + +Charts created through Image class +--------------------------------------- + +Most of the basic charts are created through methods of the `CpChart\Image` +class. Below you can find a full list of these charts, alongside example code. + +- [area](resources/doc/area.md) +- [bar](resources/doc/bar.md) +- [best fit](resources/doc/best_fit.md) +- [filled spline](resources/doc/filled_spline.md) +- [filled step](resources/doc/filled_step.md) +- [line](resources/doc/line.md) +- [plot](resources/doc/plot.md) +- [progress](resources/doc/progress.md) +- [spline](resources/doc/spline.md) +- [stacked area](resources/doc/stacked_area.md) +- [stacked bar](resources/doc/stacked_bar.md) +- [step](resources/doc/step.md) +- [zone](resources/doc/zone.md) + +Standalone charts: +------------------------------------ + +The more advanced charts have their own separate class you need to use in order +to create them. As before, below is a full list of these, with example code. + +- [2D pie](resources/doc/2d_pie.md) +- [3D pie](resources/doc/3d_pie.md) +- [2D ring](resources/doc/2d_ring.md) +- [3D ring](resources/doc/3d_ring.md) +- [bubble](resources/doc/bubble.md) +- [contour](resources/doc/contour.md) +- [polar](resources/doc/polar.md) +- [radar](resources/doc/radar.md) +- [scatter best fit](resources/doc/scatter_best_fit.md) +- [scatter line](resources/doc/scatter_line.md) +- [scatter plot](resources/doc/scatter_plot.md) +- [scatter spline](resources/doc/scatter_spline.md) +- [scatter threshold](resources/doc/scatter_threshold.md) +- [scatter threshold area](resources/doc/scatter_threshold_area.md) +- [split path](resources/doc/split_path.md) +- [spring](resources/doc/spring.md) +- [stock](resources/doc/stock.md) +- [surface](resources/doc/surface.md) + +Barcodes +-------- + +The pChart library also provides a way to render barcodes 39 and 128. Below you +can find links to doc on creating them: + +- [barcode39](resources/doc/barcode_39.md) +- [barcode128](resources/doc/barcode_128.md) + +Cache +----- + +If you find yourself creating charts out of a set of data more than once, you may +consider using the cache component of the library. Head on to the [dedicated part](resources/doc/cache.md) +of the documentation for information on how to do that. + +Fonts and palletes +------------------ + +If you want to use any of the fonts or palletes files, provide only +the name of the actual file, do not add the `fonts` or `palettes` folder to the +string given into the function. If you want to load them from a different directory +than the default, you need to add the full path to the file (ex. `__DIR__.'/folder/to/my/palletes`). + +References +========== +[The original pChart website](http://www.pchart.net/) + +Links +===== + +[GitHub](https://github.com/szymach/c-pchart) + +[Packagist](https://packagist.org/packages/szymach/c-pchart) diff --git a/vendor/szymach/c-pchart/cache/.gitkeep b/vendor/szymach/c-pchart/cache/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/vendor/szymach/c-pchart/cache/.gitkeep @@ -0,0 +1 @@ + diff --git a/vendor/szymach/c-pchart/codeception.yml b/vendor/szymach/c-pchart/codeception.yml new file mode 100644 index 0000000..ae9e4f1 --- /dev/null +++ b/vendor/szymach/c-pchart/codeception.yml @@ -0,0 +1,21 @@ +--- +namespace: Test\CpChart +paths: + tests: tests + output: tests/_output + data: tests/_data + support: tests/_support +actor_suffix: Tester +extensions: + enabled: + - Codeception\Extension\RunFailed +coverage: + enabled: true + include: + - src/* +settings: + be_strict_about_changes_to_global_state: true + convert_deprecations_to_exceptions: true + colors: true + error_level: E_ALL | E_STRICT | E_DEPRECATED + report_useless_tests: true diff --git a/vendor/szymach/c-pchart/composer.json b/vendor/szymach/c-pchart/composer.json new file mode 100644 index 0000000..efe9455 --- /dev/null +++ b/vendor/szymach/c-pchart/composer.json @@ -0,0 +1,52 @@ +{ + "name": "szymach/c-pchart", + "license": "GPL-3.0-only", + "type": "library", + "description": "Port of \"pChart\" library into PHP 5+", + "keywords": ["pchart", "pChart", "statistics", "charts", "CpChart", "c-pChart"], + "homepage": "https://github.com/szymach/c-pchart", + "authors": [ + { + "name": "Jean-Damien Pogolotti", + "homepage": "http://www.pchart.net", + "role": "Creator of the original pChart library" + }, + { + "name": "Piotr Szymaszek", + "homepage": "https://github.com/szymach", + "role": "Developer of the CpChart wrapper package" + } + ], + "autoload": { + "psr-4": { "CpChart\\": "src/" }, + "files": ["constants.php"] + }, + "autoload-dev": { + "psr-4": { + "Test\\CpChart\\": [ + "tests/unit/", + "tests/_support/" + ] + } + }, + "require": { + "php": "^5.4|^7.0|^8.0", + "ext-gd": "*" + }, + "require-dev" : { + "codeception/codeception": "^4.1.22", + "phpunit/phpunit": "^5.7|^9.5", + "squizlabs/php_codesniffer": "^3.4", + "codeception/module-asserts": "^1.3", + "codeception/module-filesystem": "^1.0" + }, + "config": { + "bin-dir": "vendor/bin" + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev", + "2.0": "2.0.x-dev" + } + } +} diff --git a/vendor/szymach/c-pchart/constants.php b/vendor/szymach/c-pchart/constants.php new file mode 100644 index 0000000..98c16a6 --- /dev/null +++ b/vendor/szymach/c-pchart/constants.php @@ -0,0 +1,206 @@ + + + + + + src + tests + + vendor/* + *.db + *.color + *.ttf + + + + + + + + + + tests/unit/*Test.php + tests/_support/Helper/Unit.php + + + diff --git a/vendor/szymach/c-pchart/resources/barcode/128B.db b/vendor/szymach/c-pchart/resources/barcode/128B.db new file mode 100644 index 0000000..8c73cdd --- /dev/null +++ b/vendor/szymach/c-pchart/resources/barcode/128B.db @@ -0,0 +1,107 @@ +0;32;11011001100 +1;33;11001101100 +2;34;11001100110 +3;35;10010011000 +4;36;10010001100 +5;37;10001001100 +6;38;10011001000 +7;39;10011000100 +8;40;10001100100 +9;41;11001001000 +10;42;11001000100 +11;43;11000100100 +12;44;10110011100 +13;45;10011011100 +14;46;10011001110 +15;47;10111001100 +16;48;10011101100 +17;49;10011100110 +18;50;11001110010 +19;51;11001011100 +20;52;11001001110 +21;53;11011100100 +22;54;11001110100 +23;55;11101101110 +24;56;11101001100 +25;57;11100101100 +26;58;11100100110 +27;59;11101100100 +28;60;11100110100 +29;61;11100110010 +30;62;11011011000 +31;63;11011000110 +32;64;11000110110 +33;65;10100011000 +34;66;10001011000 +35;67;10001000110 +36;68;10110001000 +37;69;10001101000 +38;70;10001100010 +39;71;11010001000 +40;72;11000101000 +41;73;11000100010 +42;74;10110111000 +43;75;10110001110 +44;76;10001101110 +45;77;10111011000 +46;78;10111000110 +47;79;10001110110 +48;80;11101110110 +49;81;11010001110 +50;82;11000101110 +51;83;11011101000 +52;84;11011100010 +53;85;11011101110 +54;86;11101011000 +55;87;11101000110 +56;88;11100010110 +57;89;11101101000 +58;90;11101100010 +59;91;11100011010 +60;92;11101111010 +61;93;11001000010 +62;94;11110001010 +63;95;10100110000 +64;96;10100001100 +65;97;10010110000 +66;98;10010000110 +67;99;10000101100 +68;100;10000100110 +69;101;10110010000 +70;102;10110000100 +71;103;10011010000 +72;104;10011000010 +73;105;10000110100 +74;106;10000110010 +75;107;11000010010 +76;108;11001010000 +77;109;11110111010 +78;110;11000010100 +79;111;10001111010 +80;112;10100111100 +81;113;10010111100 +82;114;10010011110 +83;115;10111100100 +84;116;10011110100 +85;117;10011110010 +86;118;11110100100 +87;119;11110010100 +88;120;11110010010 +89;121;11011011110 +90;122;11011110110 +91;123;11110110110 +92;124;10101111000 +93;125;10100011110 +94;126;10001011110 +95;200;10111101000 +96;201;10111100010 +97;202;11110101000 +98;203;11110100010 +99;204;10111011110 +100;205;10111101110 +101;206;11101011110 +102;207;11110101110 +103;208;11010000100 +104;209;11010010000 +105;210;11010011100 +106;211;1100011101011 \ No newline at end of file diff --git a/vendor/szymach/c-pchart/resources/barcode/39.db b/vendor/szymach/c-pchart/resources/barcode/39.db new file mode 100644 index 0000000..3857ed7 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/barcode/39.db @@ -0,0 +1,44 @@ +0;101001101101 +1;110100101011 +2;101100101011 +3;110110010101 +4;101001101011 +5;110100110101 +6;101100110101 +7;101001011011 +8;110100101101 +9;101100101101 +A;110101001011 +B;101101001011 +C;110110100101 +D;101011001011 +E;110101100101 +F;101101100101 +G;101010011011 +H;110101001101 +I;101101001101 +J;101011001101 +K;110101010011 +L;101101010011 +M;110110101001 +N;101011010011 +O;110101101001 +P;101101101001 +Q;101010110011 +R;110101011001 +S;101101011001 +T;101011011001 +U;110010101011 +V;100110101011 +W;110011010101 +X;100101101011 +Y;110010110101 +Z;100110110101 +-;100101011011 +.;110010101101 + ;100110101101 +$;100100100101 +/;100100101001 ++;100101001001 +%;101001001001 +*;100101101101 \ No newline at end of file diff --git a/vendor/szymach/c-pchart/resources/doc/2d_pie.md b/vendor/szymach/c-pchart/resources/doc/2d_pie.md new file mode 100644 index 0000000..3496ef4 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/2d_pie.md @@ -0,0 +1,87 @@ +# Drawing a 2D pie chart + +[Reference](http://wiki.pchart.net/doc.pie.draw2dpie.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Pie; +use CpChart\Data; +use CpChart\Image; + +// Create and populate data +$data = new Data(); +$data->addPoints([40, 60, 15, 10, 6, 4], "ScoreA"); +$data->setSerieDescription("ScoreA", "Application A"); + +// Define the absissa serie +$data->addPoints(["<10", "10<>20", "20<>40", "40<>60", "60<>80", ">80"], "Labels"); +$data->setAbscissa("Labels"); + +// Create the image +$image = new Image(700, 230, $data); + +// Draw a solid background +$backgroundSettings = [ + "R" => 173, + "G" => 152, + "B" => 217, + "Dash" => 1, + "DashR" => 193, + "DashG" => 172, + "DashB" => 237 +]; +$image->drawFilledRectangle(0, 0, 700, 230, $backgroundSettings); + +//Draw a gradient overlay +$gradientSettings = [ + "StartR" => 209, + "StartG" => 150, + "StartB" => 231, + "EndR" => 111, + "EndG" => 3, + "EndB" => 138, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $gradientSettings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +// Add a border to the picture +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +// Write the picture title +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pPie - Draw 2D pie charts", ["R" => 255, "G" => 255, "B" => 255]); + +// Set the default font properties +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 10, "R" => 80, "G" => 80, "B" => 80]); + +// Enable shadow computing +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 150, "G" => 150, "B" => 150, "Alpha" => 100]); +$image->drawText(140, 200, "Single AA pass", ["R" => 0, "G" => 0, "B" => 0, "Align" => TEXT_ALIGN_TOPMIDDLE]); + +// Create and draw the chart +$pieChart = new Pie($image, $data); +$pieChart->draw2DPie(140, 125, ["SecondPass" => false]); +$pieChart->draw2DPie(340, 125, ["DrawLabels" => true, "Border" => true]); +$pieChart->draw2DPie(540, 125, [ + "DataGapAngle" => 10, + "DataGapRadius" => 6, + "Border" => true, + "BorderR" => 255, + "BorderG" => 255, + "BorderB" => 255 +]); +$image->drawText(540, 200, "Extended AA pass / Splitted", ["R" => 0, "G" => 0, "B" => 0, "Align" => TEXT_ALIGN_TOPMIDDLE]); + +$pieChart->pChartObject->autoOutput("example.draw2DPie.png"); +``` + diff --git a/vendor/szymach/c-pchart/resources/doc/2d_ring.md b/vendor/szymach/c-pchart/resources/doc/2d_ring.md new file mode 100644 index 0000000..bedc316 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/2d_ring.md @@ -0,0 +1,72 @@ +# Drawing a 2D ring chart + +[Reference](http://wiki.pchart.net/doc.pie.draw2dring.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Pie; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([50, 2, 3, 4, 7, 10, 25, 48, 41, 10], "ScoreA"); +$data->setSerieDescription("ScoreA", "Application A"); + +/* Define the absissa serie */ +$data->addPoints(["A0", "B1", "C2", "D3", "E4", "F5", "G6", "H7", "I8", "J9"], "Labels"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(300, 260, $data); + +/* Draw a solid background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 300, 300, $settings); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 300, 260, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 300, 20, DIRECTION_VERTICAL, ["StartR" => 0, "StartG" => 0, + "StartB" => 0, "EndR" => 50, "EndG" => 50, "EndB" => 50, "Alpha" => 100]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 299, 259, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pPie - Draw 2D ring charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Set the default font properties */ +$image->setFontProperties([ + "FontName" => "Forgotte.ttf", + "FontSize" => 10, + "R" => 80, + "G" => 80, + "B" => 80 +]); + +/* Enable shadow computing */ +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 50]); + +/* Create the pPie object */ +$pieChart = new Pie($image, $data); + +/* Draw an AA pie chart */ +$pieChart->draw2DRing(160, 140, ["DrawLabels" => true, "LabelStacked" => true, "Border" => true]); + +/* Write the legend box */ +$image->setShadow(false); +$pieChart->drawPieLegend(15, 40, ["Alpha" => 20]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.draw2DRing.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/3d_pie.md b/vendor/szymach/c-pchart/resources/doc/3d_pie.md new file mode 100644 index 0000000..6e0fc9c --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/3d_pie.md @@ -0,0 +1,119 @@ +# Drawing a 3D pie chart + +[Reference](http://wiki.pchart.net/doc.pie.draw3dpie.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Pie; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([40, 30, 20], "ScoreA"); +$data->setSerieDescription("ScoreA", "Application A"); + +/* Define the absissa serie */ +$data->addPoints(["A", "B", "C"], "Labels"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(700, 230, $data, true); + +/* Draw a solid background */ +$image->drawFilledRectangle(0, 0, 700, 230, [ + "R" => 173, + "G" => 152, + "B" => 217, + "Dash" => 1, + "DashR" => 193, + "DashG" => 172, + "DashB" => 237 +]); + +/* Draw a gradient overlay */ +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, [ + "StartR" => 209, + "StartG" => 150, + "StartB" => 231, + "EndR" => 111, + "EndG" => 3, + "EndB" => 138, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pPie - Draw 3D pie charts", ["R" => 255, "G" => 255, + "B" => 255]); + +/* Set the default font properties */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 10, + "R" => 80, "G" => 80, "B" => 80]); + +/* Create the pPie object */ +$pieChart = new Pie($image, $data); + +/* Define the slice color */ +$pieChart->setSliceColor(0, ["R" => 143, "G" => 197, "B" => 0]); +$pieChart->setSliceColor(1, ["R" => 97, "G" => 77, "B" => 63]); +$pieChart->setSliceColor(2, ["R" => 97, "G" => 113, "B" => 63]); + +/* Draw a simple pie chart */ +$pieChart->draw3DPie(120, 125, ["SecondPass" => false]); + +/* Draw an AA pie chart */ +$pieChart->draw3DPie(340, 125, ["DrawLabels" => true, "Border" => true]); + +/* Enable shadow computing */ +$image->setShadow(true, ["X" => 3, "Y" => 3, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw a splitted pie chart */ +$pieChart->draw3DPie(560, 125, ["WriteValues" => true, "DataGapAngle" => 10, "DataGapRadius" => 6, "Border" => true]); + +/* Write the legend */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]); +$image->drawText(120, 200, "Single AA pass", [ + "DrawBox" => true, + "BoxRounded" => true, + "R" => 0, + "G" => 0, + "B" => 0, + "Align" => TEXT_ALIGN_TOPMIDDLE +]); +$image->drawText(440, 200, "Extended AA pass / Splitted", [ + "DrawBox" => true, + "BoxRounded" => true, + "R" => 0, + "G" => 0, + "B" => 0, + "Align" => TEXT_ALIGN_TOPMIDDLE +]); + +/* Write the legend box */ +$image->setFontProperties([ + "FontName" => "Silkscreen.ttf", + "FontSize" => 6, + "R" => 255, + "G" => 255, + "B" => 255 +]); +$pieChart->drawPieLegend(600, 8, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.draw3DPie.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/3d_ring.md b/vendor/szymach/c-pchart/resources/doc/3d_ring.md new file mode 100644 index 0000000..3134cad --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/3d_ring.md @@ -0,0 +1,78 @@ +# Drawing a 3D ring chart + +[Reference](http://wiki.pchart.net/doc.pie.draw3dring.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Pie; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([50, 2, 3, 4, 7, 10, 25, 48, 41, 10], "ScoreA"); +$data->setSerieDescription("ScoreA", "Application A"); + +/* Define the absissa serie */ +$data->addPoints(["A0", "B1", "C2", "D3", "E4", "F5", "G6", "H7", "I8", "J9"], "Labels"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw a solid background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pPie - Draw 3D ring charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Set the default font properties */ +$image->setFontProperties([ + "FontName" => "Forgotte.ttf", + "FontSize" => 10, + "R" => 80, + "G" => 80, + "B" => 80 +]); + +/* Enable shadow computing */ +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 50]); + +/* Create the pPie object */ +$pieChart = new Pie($image, $data); + +/* Draw an AA pie chart */ +$pieChart->draw3DRing(200, 200, ["DrawLabels" => true, "LabelStacked" => true, "Border" => true]); + +/* Write the legend box */ +$pieChart->drawPieLegend(80, 360, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER, "Alpha" => 20]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.draw3DRing.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/area.md b/vendor/szymach/c-pchart/resources/doc/area.md new file mode 100644 index 0000000..e7e9a96 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/area.md @@ -0,0 +1,72 @@ +# Drawing an area chart + +[Reference](http://wiki.pchart.net/doc.chart.drawareachart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +$data = new Data(); +for ($i = 0; $i <= 30; $i++) { + $data->addPoints(rand(1, 15), "Probe 1"); +} +$data->setSerieTicks("Probe 2", 4); +$data->setAxisName(0, "Temperatures"); + +// Create the Image object +$image = new Image(700, 230, $data); + +/* Turn off Antialiasing */ +$image->Antialias = false; + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(150, 35, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Define the chart area */ +$image->setGraphArea(60, 40, 650, 200); + +/* Draw the scale */ +$scaleSettings = [ + "XMargin" => 10, + "YMargin" => 10, + "Floating" => true, + "GridR" => 200, + "GridG" => 200, + "GridB" => 200, + "DrawSubTicks" => true, + "CycleBackground" => true +]; +$image->drawScale($scaleSettings); + +/* Write the chart legend */ +$image->drawLegend(600, 20, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Turn on Antialiasing */ +$image->Antialias = true; + +/* Enable shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the area chart */ +$Threshold = []; +$Threshold[] = ["Min" => 0, "Max" => 5, "R" => 207, "G" => 240, "B" => 20, "Alpha" => 70]; +$Threshold[] = ["Min" => 5, "Max" => 10, "R" => 240, "G" => 232, "B" => 20, "Alpha" => 70]; +$Threshold[] = ["Min" => 10, "Max" => 20, "R" => 240, "G" => 191, "B" => 20, "Alpha" => 70]; +$image->drawAreaChart(["Threshold" => $Threshold]); + +/* Write the thresholds */ +$image->drawThreshold(5, ["WriteCaption" => true, "Caption" => "Warn Zone", "Alpha" => 70, "Ticks" => 2, "R" => 0, "G" => 0, "B" => 255]); +$image->drawThreshold(10, ["WriteCaption" => true, "Caption" => "Error Zone", "Alpha" => 70, "Ticks" => 2, "R" => 0, "G" => 0, "B" => 255]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawAreaChart.threshold.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/bar.md b/vendor/szymach/c-pchart/resources/doc/bar.md new file mode 100644 index 0000000..515270d --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/bar.md @@ -0,0 +1,64 @@ +# Drawing a bar chart + +[Reference](http://wiki.pchart.net/doc.chart.drawbarchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([13251, 4118, 3087, 1460, 1248, 156, 26, 9, 8], "Hits"); +$data->setAxisName(0, "Hits"); +$data->addPoints(["Firefox", "Chrome", "Internet Explorer", "Opera", "Safari", "Mozilla", "SeaMonkey", "Camino", "Lunascape"], "Browsers"); +$data->setSerieDescription("Browsers", "Browsers"); +$data->setAbscissa("Browsers"); + +/* Create the Image object */ +$image = new Image(500, 500, $data); +$image->drawGradientArea(0, 0, 500, 500, DIRECTION_VERTICAL, [ + "StartR" => 240, + "StartG" => 240, + "StartB" => 240, + "EndR" => 180, + "EndG" => 180, + "EndB" => 180, + "Alpha" => 100 +]); +$image->drawGradientArea(0, 0, 500, 500, DIRECTION_HORIZONTAL, [ + "StartR" => 240, + "StartG" => 240, + "StartB" => 240, + "EndR" => 180, + "EndG" => 180, + "EndB" => 180, + "Alpha" => 20 +]); +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Draw the chart scale */ +$image->setGraphArea(100, 30, 480, 480); +$image->drawScale([ + "CycleBackground" => true, + "DrawSubTicks" => true, + "GridR" => 0, + "GridG" => 0, + "GridB" => 0, + "GridAlpha" => 10, + "Pos" => SCALE_POS_TOPBOTTOM +]); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the chart */ +$image->drawBarChart(["DisplayPos" => LABEL_POS_INSIDE, "DisplayValues" => true, "Rounded" => true, "Surrounding" => 30]); + +/* Write the legend */ +$image->drawLegend(570, 215, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawBarChart.vertical.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/barcode_128.md b/vendor/szymach/c-pchart/resources/doc/barcode_128.md new file mode 100644 index 0000000..ee5bf8f --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/barcode_128.md @@ -0,0 +1,89 @@ +# Drawing a barcode 128 + +[Reference](http://wiki.pchart.net/doc.barcode128.pBarcode128.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Barcode\Barcode128; +use CpChart\Image; + +/* Create the Image object */ +$image = new Image(700, 230); + +/* Draw the background */ +$image->drawFilledRectangle(0, 0, 700, 230, [ + "R" => 170, + "G" => 183, + "B" => 87, + "Dash" => 1, + "DashR" => 190, + "DashG" => 203, + "DashB" => 107 +]); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Draw the top bar */ +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "Barcode 128 - Add barcode to your pictures", ["R" => 255, "G" => 255, "B" => 255]); + +/* Create the barcode 128 object */ +$barcodeChart = new Barcode128(); + +/* Draw a simple barcode */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$barcodeChart->draw($image, "pChart Rocks!", 50, 50, ["ShowLegend" => true, "DrawArea" => true]); + +/* Draw a rotated barcode */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 12]); +$barcodeChart->draw($image, "Turn me on", 650, 50, ["ShowLegend" => true, "DrawArea" => true, "Angle" => 90]); + +/* Draw a rotated barcode */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 12]); +$barcodeChart->draw($image, "Do what you want !", 290, 140, [ + "R" => 255, + "G" => 255, + "B" => 255, + "AreaR" => 150, + "AreaG" => 30, + "AreaB" => 27, + "ShowLegend" => true, + "DrawArea" => true, + "Angle" => 350, + "AreaBorderR" => 70, + "AreaBorderG" => 20, + "AreaBorderB" => 20 +]); + +/* Render the picture */ +$image->autoOutput("example.barcode128.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/barcode_39.md b/vendor/szymach/c-pchart/resources/doc/barcode_39.md new file mode 100644 index 0000000..f81e47a --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/barcode_39.md @@ -0,0 +1,82 @@ +# Drawing a barcode 39 + +[Reference](http://wiki.pchart.net/doc.barcode39.pBarcode39.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Barcode\Barcode39; +use CpChart\Image; + +/* Create the Image object */ +$image = new Image(700, 230); + +/* Draw the background */ +$image->drawFilledRectangle(0, 0, 700, 230, [ + "R" => 170, + "G" => 183, + "B" => 87, + "Dash" => 1, + "DashR" => 190, + "DashG" => 203, + "DashB" => 107 +]); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Draw the picture border */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "Barcode 39 - Add barcode to your pictures", ["R" => 255, "G" => 255, "B" => 255]); + +/* Create the barcode 39 object */ +$barcodeChart = new Barcode39(); + +/* Draw a simple barcode */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$barcodeChart->draw($image, "pChart Rocks!", 50, 50, ["ShowLegend" => true, "DrawArea" => true]); + +/* Draw a rotated barcode */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 12]); +$barcodeChart->draw($image, "Turn me on", 650, 50, ["ShowLegend" => true, "DrawArea" => true, "Angle" => 90]); + +/* Draw a rotated barcode */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 12]); +$barcodeChart->draw($image, "Do what you want !", 290, 140, [ + "R" => 255, + "G" => 255, + "B" => 255, + "AreaR" => 150, + "AreaG" => 30, + "AreaB" => 27, + "ShowLegend" => true, + "DrawArea" => true, + "Angle" => 350, + "AreaBorderR" => 70, + "AreaBorderG" => 20, + "AreaBorderB" => 20 +]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.barcode39.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/best_fit.md b/vendor/szymach/c-pchart/resources/doc/best_fit.md new file mode 100644 index 0000000..bdfe54e --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/best_fit.md @@ -0,0 +1,70 @@ +# Drawing a best fit chart + +[Reference](http://wiki.pchart.net/doc.chart.drawbestfit.html) + +``` php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(10, 30) + $i, "Probe 1"); +} +for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(0, 10) + $i, "Probe 2"); +} +$data->setAxisName(0, "Temperatures"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Turn off Antialiasing */ +$image->Antialias = false; + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => ".Forgotte.ttf", "FontSize" => 11]); +$image->drawText(150, 35, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => ".pf_arma_five.ttf", "FontSize" => 6]); + +/* Define the chart area */ +$image->setGraphArea(60, 40, 650, 200); + +/* Draw the scale */ +$scaleSettings = [ + "XMargin" => 10, + "YMargin" => 10, + "Floating" => true, + "GridR" => 200, + "GridG" => 200, + "GridB" => 200, + "DrawSubTicks" => true, + "CycleBackground" => true +]; +$image->drawScale($scaleSettings); + +/* Turn on Antialiasing */ +$image->Antialias = true; + +/* Draw the line of best fit */ +$image->drawBestFit(); + +/* Turn on shadows */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the line chart */ +$image->drawPlotChart(); + +/* Write the chart legend */ +$image->drawLegend(580, 20, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawBestFit.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/bubble.md b/vendor/szymach/c-pchart/resources/doc/bubble.md new file mode 100644 index 0000000..a51b0ee --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/bubble.md @@ -0,0 +1,107 @@ +# Drawing a linear bubble chart + +[Reference](http://wiki.pchart.net/doc.bubble.drawbubblechart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Bubble; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([34, 55, 15, 62, 38, 42], "Probe1"); +$data->addPoints([5, 10, 8, 9, 15, 10], "Probe1Weight"); +$data->addPoints([5, 10, -5, -1, 0, -10], "Probe2"); +$data->addPoints([6, 10, 14, 10, 14, 6], "Probe2Weight"); +$data->setSerieDescription("Probe1", "This year"); +$data->setSerieDescription("Probe2", "Last year"); +$data->setAxisName(0, "Current stock"); +$data->addPoints(["Apple", "Banana", "Orange", "Lemon", "Peach", "Strawberry"], "Product"); +$data->setAbscissa("Product"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawBubbleChart() - draw a linear bubble chart", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Write the title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(40, 55, "Current Stock / Needs chart", ["FontSize" => 14, "Align" => TEXT_ALIGN_BOTTOMLEFT]); + +/* Change the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Create the Bubble chart object and scale up */ +$bubbleChart = new Bubble($image, $data); + +/* Scale up for the bubble chart */ +$bubbleDataSeries = ["Probe1", "Probe2"]; +$bubbleWeightSeries = ["Probe1Weight", "Probe2Weight"]; +$bubbleChart->bubbleScale($bubbleDataSeries, $bubbleWeightSeries); + +/* Draw the 1st chart */ +$image->setGraphArea(40, 60, 430, 190); +$image->drawFilledRectangle(40, 60, 430, 190, ["R" => 255, "G" => 255, "B" => 255, + "Surrounding" => -200, "Alpha" => 10]); +$image->drawScale(["DrawSubTicks" => true, "CycleBackground" => true]); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]); +$bubbleChart->drawBubbleChart($bubbleDataSeries, $bubbleWeightSeries); + +/* Draw the 2nd scale */ +$image->setShadow(false); +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "DrawSubTicks" => true]); + +/* Draw the 2nd stock chart */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]); +$bubbleChart->drawbubbleChart($bubbleDataSeries, $bubbleWeightSeries); + +/* Write the chart legend */ +$image->drawLegend(550, 215, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawBubbleChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/cache.md b/vendor/szymach/c-pchart/resources/doc/cache.md new file mode 100644 index 0000000..0a3eb56 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/cache.md @@ -0,0 +1,83 @@ +# Cache operations + +[Reference](http://wiki.pchart.net/doc.pcache.pcache.html) + +To speed up the process of creating charts, you can store them in the cache files +using the `CpChart\Cache` class. It will create two files - `cache.db` and +`index.db` in a dedicated directory (`app\cache` by default, relative to the library's +root directory), but you can change these using the `$settings` array passed +to the object's constructor. + +Should you decide to use the cache component, the following sections describe +how you can do that. + +## Using cache to store and retrieve chart data + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Cache; +use CpChart\Data; +use CpChart\Image; + +// Standard chart creation +$data = new Data(); +$data->addPoints([1, 3, 4, 3, 5]); + +$image = new Image(700, 230, $data); +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->setGraphArea(60, 40, 670, 190); +$image->drawScale(); +$image->drawSplineChart(); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "Test of the pCache class", ["R" => 255, "G" => 255, "B" => 255]); + +// Create a cache object and store the chart in it +$cache = new Cache([ + // Optionally change the default directory and file names + 'CacheFolder' => 'path/to/your/cache/directory', + 'CacheIndex' => 'name_of_the_index_file.db', + 'CacheDB' => 'name_of_the_database_file.db' +]); +$chartHash = $cache->getHash($data); // Chart dentifier in the cache +$cache->writeToCache($chartHash, $image); + +// Create an image file from cache +$cache->saveFromCache($chartHash, "example.drawCachedSpline.png"); + +// Directly stroke the saved data to the browser +$cache->strokeFromCache($chartHash) + +// Automatically choose a way to output stored data +$cache->autoOutput($chartHash) +``` + +## Removal operations + +```php +// Assuming we have $chartHash and $cache variables from the previous example + +// This will remove the chart by it's hash +$cache->remove($chartHash); + +// This will remove every chart in cache older than the amount of seconds passed +// into the argument's parameter +$cache->removeOlderThan(60 * 60 * 24); // Remove data older than 24 hours + +// This flushes the cache completely and regenerates the .db files +$cache->flush(); +``` + +There is also the function called `CpChart\Cache::dbRemoval(array $settings)`, +but it only covers two use cases - removing by chart hash and age. Since there +are dedicated methods for each of them (`remove` and `removeOlderThan`, respectively), +there is no reason to cover it any further. diff --git a/vendor/szymach/c-pchart/resources/doc/contour.md b/vendor/szymach/c-pchart/resources/doc/contour.md new file mode 100644 index 0000000..da002ea --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/contour.md @@ -0,0 +1,92 @@ +# Drawing a contour chart + +[Reference](http://wiki.pchart.net/doc.surface.drawcontour.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Surface; +use CpChart\Data; +use CpChart\Image; + +/* Create the Image object */ +$image = new Image(400, 400); + +/* Create a solid background */ +$image->drawFilledRectangle(0, 0, 400, 400, [ + "R" => 179, + "G" => 217, + "B" => 91, + "Dash" => 1, + "DashR" => 199, + "DashG" => 237, + "DashB" => 111 +]); + +/* Do a gradient overlay */ +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 194, + "StartG" => 231, + "StartB" => 44, + "EndR" => 43, + "EndG" => 107, + "EndB" => 58, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pSurface() :: 2D surface charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Define the charting area */ +$image->setGraphArea(20, 40, 380, 380); +$image->drawFilledRectangle(20, 40, 380, 380, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 20 +]); + +$image->setShadow(true, ["X" => 1, "Y" => 1]); + +/* Create the surface object */ +$surfaceChart = new Surface($image); + +/* Set the grid size */ +$surfaceChart->setGrid(20, 20); + +/* Write the axis labels */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$surfaceChart->writeXLabels(["Position" => LABEL_POSITION_BOTTOM]); +$surfaceChart->writeYLabels(); + +/* Add random values */ +for ($i = 0; $i <= 50; $i++) { + $surfaceChart->addPoint(rand(0, 20), rand(0, 20), rand(0, 100)); +} + +/* Compute the missing points */ +$surfaceChart->computeMissing(); + +/* Draw the surface chart */ +$surfaceChart->drawSurface(["Border" => true, "Surrounding" => 40]); + +/* Draw the contour with a threshold of 50 */ +$surfaceChart->drawContour(50, ["R" => 0, "G" => 0, "B" => 0]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.surface.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/filled_spline.md b/vendor/szymach/c-pchart/resources/doc/filled_spline.md new file mode 100644 index 0000000..03b50d0 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/filled_spline.md @@ -0,0 +1,125 @@ +# Drawing a filled spline chart + +[Reference](http://wiki.pchart.net/doc.chart.drawFilledSplineChart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->setAxisName(0, "Strength"); +for ($i = 0; $i <= 720; $i = $i + 20) { + $data->addPoints(cos(deg2rad($i)) * 100, "Probe 1"); + $data->addPoints(cos(deg2rad($i + 90)) * 60, "Probe 2"); +} + +/* Create the Image object */ +$image = new Image(847, 304, $data); +$image->drawGradientArea(0, 0, 847, 304, DIRECTION_VERTICAL, [ + "StartR" => 47, + "StartG" => 47, + "StartB" => 47, + "EndR" => 17, + "EndG" => 17, + "EndB" => 17, + "Alpha" => 100 +]); +$image->drawGradientArea(0, 250, 847, 304, DIRECTION_VERTICAL, [ + "StartR" => 47, + "StartG" => 47, + "StartB" => 47, + "EndR" => 27, + "EndG" => 27, + "EndB" => 27, + "Alpha" => 100 +]); +$image->drawLine(0, 249, 847, 249, ["R" => 0, "G" => 0, "B" => 0]); +$image->drawLine(0, 250, 847, 250, ["R" => 70, "G" => 70, "B" => 70]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 846, 303, ["R" => 204, "G" => 204, "B" => 204]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->drawText(423, 14, "Cyclic magnetic field strength", [ + "R" => 255, + "G" => 255, + "B" => 255, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE +]); + +/* Define the chart area */ +$image->setGraphArea(58, 27, 816, 228); + +/* Draw a rectangle */ +$image->drawFilledRectangle(58, 27, 816, 228, [ + "R" => 0, + "G" => 0, + "B" => 0, + "Dash" => true, + "DashR" => 0, + "DashG" => 51, + "DashB" => 51, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 +]); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]); + +/* Draw the scale */ +$image->setFontProperties(["R" => 255, "G" => 255, "B" => 255]); +$ScaleSettings = [ + "XMargin" => 4, + "DrawSubTicks" => true, + "GridR" => 255, + "GridG" => 255, + "GridB" => 255, + "AxisR" => 255, + "AxisG" => 255, + "AxisB" => 255, + "GridAlpha" => 30, + "CycleBackground" => true +]; +$image->drawScale($ScaleSettings); + +/* Draw the spline chart */ +$image->drawFilledSplineChart(); + +/* Write the chart boundaries */ +$BoundsSettings = [ + "MaxDisplayR" => 237, + "MaxDisplayG" => 23, + "MaxDisplayB" => 48, + "MinDisplayR" => 23, + "MinDisplayG" => 144, + "MinDisplayB" => 237 +]; +$image->writeBounds(BOUND_BOTH, $BoundsSettings); + +/* Write the 0 line */ +$image->drawThreshold(0, ["WriteCaption" => true]); + +/* Write the chart legend */ +$image->setFontProperties(["R" => 255, "G" => 255, "B" => 255]); +$image->drawLegend(560, 266, ["Style" => LEGEND_NOBORDER]); + +/* Write the 1st data series statistics */ +$settings = ["R" => 188, "G" => 224, "B" => 46, "Align" => TEXT_ALIGN_BOTTOMLEFT]; +$image->drawText(620, 270, "Max : " . ceil($data->getMax("Probe 1")), $settings); +$image->drawText(680, 270, "Min : " . ceil($data->getMin("Probe 1")), $settings); +$image->drawText(740, 270, "Avg : " . ceil($data->getSerieAverage("Probe 1")), $settings); + +/* Write the 2nd data series statistics */ +$settings = ["R" => 224, "G" => 100, "B" => 46, "Align" => TEXT_ALIGN_BOTTOMLEFT]; +$image->drawText(620, 283, "Max : " . ceil($data->getMax("Probe 2")), $settings); +$image->drawText(680, 283, "Min : " . ceil($data->getMin("Probe 2")), $settings); +$image->drawText(740, 283, "Avg : " . ceil($data->getSerieAverage("Probe 2")), $settings); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawFilledSplineChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/filled_step.md b/vendor/szymach/c-pchart/resources/doc/filled_step.md new file mode 100644 index 0000000..e17eeea --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/filled_step.md @@ -0,0 +1,75 @@ +# Drawing a filled step chart + +[Reference](http://wiki.pchart.net/doc.chart.drawfilledstepchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([-4, 2, VOID, 12, 8, 3], "Probe 1"); +$data->addPoints([3, 12, 15, 8, 5, -5], "Probe 2"); +$data->addPoints([2, 7, 5, 18, 19, 22], "Probe 3"); +$data->setSerieTicks("Probe 2", 4); +$data->setAxisName(0, "Temperatures"); +$data->addPoints(["Jan", "Feb", "Mar", "Apr", "May", "Jun"], "Labels"); +$data->setSerieDescription("Labels", "Months"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = ["StartR" => 219, "StartG" => 231, "StartB" => 139, "EndR" => 1, "EndG" => 138, "EndB" => 68, "Alpha" => 50]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawFilledStepChart() - draw a filled step chart", ["R" => 255, "G" => 255, "B" => 255]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(250, 55, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Draw the scale and the 1st chart */ +$image->setGraphArea(60, 60, 450, 190); +$image->drawFilledRectangle(60, 60, 450, 190, ["R" => 255, "G" => 255, "B" => 255, "Surrounding" => -200, "Alpha" => 10]); +$image->drawScale(["DrawSubTicks" => true]); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->drawFilledStepChart(["ForceTransparency" => 40, "DisplayValues" => true, "DisplayColor" => DISPLAY_AUTO]); +$image->setShadow(false); + +/* Draw the scale and the 2nd chart */ +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, ["R" => 255, "G" => 255, "B" => 255, "Surrounding" => -200, "Alpha" => 10]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "DrawSubTicks" => true]); +$image->setShadow(true, ["X" => -1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->drawFilledStepChart(["ForceTransparency" => 40]); +$image->setShadow(false); + +/* Write the chart legend */ +$image->drawLegend(510, 205, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawFilledStepChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/line.md b/vendor/szymach/c-pchart/resources/doc/line.md new file mode 100644 index 0000000..083a3d7 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/line.md @@ -0,0 +1,50 @@ +# Drawing a line chart + +[Reference](http://wiki.pchart.net/doc.chart.drawlinechart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Build a dataset */ +$data = new Data(); +$data->addPoints([-4, VOID, VOID, 12, 8, 3], "Probe 1"); +$data->addPoints([3, 12, 15, 8, 5, -5], "Probe 2"); +$data->addPoints([2, 7, 5, 18, 19, 22], "Probe 3"); +$data->setSerieTicks("Probe 2", 4); +$data->setSerieWeight("Probe 3", 2); +$data->setAxisName(0, "Temperatures"); +$data->addPoints(["Jan", "Feb", "Mar", "Apr", "May", "Jun"], "Labels"); +$data->setSerieDescription("Labels", "Months"); +$data->setAbscissa("Labels"); + +/* Create the 1st chart */ +$image = new Image(700, 230, $data); +$image->setGraphArea(60, 60, 450, 190); +$image->drawFilledRectangle(60, 60, 450, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["DrawSubTicks" => true]); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->setFontProperties(["FontName" => "fonts/pf_arma_five.ttf", "FontSize" => 6]); +$image->drawLineChart(["DisplayValues" => true, "DisplayColor" => DISPLAY_AUTO]); +$image->setShadow(false); + +/* Create the 2nd chart */ +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, ["R" => 255, "G" => 255, "B" => 255, "Surrounding" => -200, "Alpha" => 10]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "DrawSubTicks" => true]); +$image->setShadow(true, ["X" => -1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->drawLineChart(); +$image->setShadow(false); + +/* Write the legend */ +$image->drawLegend(510, 205, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); +$image->autoOutput("example.drawLineChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/plot.md b/vendor/szymach/c-pchart/resources/doc/plot.md new file mode 100644 index 0000000..23c2007 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/plot.md @@ -0,0 +1,67 @@ +# Drawing a plot chart + +[Reference](http://wiki.pchart.net/doc.chart.drawplotchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(0, 20), "Probe 1"); +} +for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(0, 20), "Probe 2"); +} +$data->setSerieShape("Probe 1", SERIE_SHAPE_FILLEDTRIANGLE); +$data->setSerieShape("Probe 2", SERIE_SHAPE_FILLEDSQUARE); +$data->setAxisName(0, "Temperatures"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Turn off Antialiasing */ +$image->Antialias = false; + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(150, 35, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Define the chart area */ +$image->setGraphArea(60, 40, 650, 200); + +/* Draw the scale */ +$scaleSettings = [ + "XMargin" => 10, + "YMargin" => 10, + "Floating" => true, + "GridR" => 200, + "GridG" => 200, + "GridB" => 200, + "DrawSubTicks" => true, + "CycleBackground" => true +]; +$image->drawScale($scaleSettings); + +/* Turn on Antialiasing */ +$image->Antialias = true; +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the line chart */ +$image->drawPlotChart(); + +/* Write the chart legend */ +$image->drawLegend(580, 20, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawPlotChart.simple.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/polar.md b/vendor/szymach/c-pchart/resources/doc/polar.md new file mode 100644 index 0000000..3795266 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/polar.md @@ -0,0 +1,96 @@ +# Drawing a polar chart + +[Reference](http://wiki.pchart.net/doc.draw.polar.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Radar; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([10, 20, 30, 40, 50, 60, 70, 80, 90], "ScoreA"); +$data->addPoints([20, 40, 50, 12, 10, 30, 40, 50, 60], "ScoreB"); +$data->setSerieDescription("ScoreA", "Coverage A"); +$data->setSerieDescription("ScoreB", "Coverage B"); + +/* Define the absissa serie */ +$data->addPoints([40, 80, 120, 160, 200, 240, 280, 320, 360], "Coord"); +$data->setAbscissa("Coord"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw a solid background */ +$settings = ["R" => 179, "G" => 217, "B" => 91, "Dash" => 1, "DashR" => 199, "DashG" => 237, "DashB" => 111]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay some gradient areas */ +$settings = ["StartR" => 194, "StartG" => 231, "StartB" => 44, "EndR" => 43, "EndG" => 107, "EndB" => 58, "Alpha" => 50]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pRadar - Draw polar charts", ["R" => 255, "G" => 255, + "B" => 255]); + +/* Set the default font properties */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 10, + "R" => 80, "G" => 80, "B" => 80]); + +/* Enable shadow computing */ +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, + "Alpha" => 10]); + +/* Create the pRadar object */ +$radarChart = new Radar(); + +/* Draw a polar chart */ +$image->setGraphArea(10, 25, 340, 225); +$options = ["BackgroundGradient" => [ + "StartR" => 255, + "StartG" => 255, + "StartB" => 255, + "StartAlpha" => 100, + "EndR" => 207, + "EndG" => 227, + "EndB" => 125, + "EndAlpha" => 50 +]]; +$radarChart->drawPolar($image, $data, $options); + +/* Draw a polar chart */ +$image->setGraphArea(350, 25, 690, 225); +$options = [ + "LabelPos" => RADAR_LABELS_HORIZONTAL, + "BackgroundGradient" => [ + "StartR" => 255, "StartG" => 255, "StartB" => 255, "StartAlpha" => 50, "EndR" => 32, + "EndG" => 109, "EndB" => 174, "EndAlpha" => 30 + ], + "AxisRotation" => 0, + "DrawPoly" => true, + "PolyAlpha" => 50 +]; +$radarChart->drawPolar($image, $data, $options); + +/* Write the chart legend */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->drawLegend(270, 205, ["Style" => LEGEND_BOX, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.polar.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/progress.md b/vendor/szymach/c-pchart/resources/doc/progress.md new file mode 100644 index 0000000..6d6a2ed --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/progress.md @@ -0,0 +1,71 @@ +# Drawing a progress chart + +[Reference](http://wiki.pchart.net/doc.chart.drawprogress.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +$image = new Image(700, 250); + +/* Enable shadow support */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]); + +/* Left Red bar */ +$progressOptions = ["R" => 209, "G" => 31, "B" => 27, "Surrounding" => 20, "BoxBorderR" => 0, + "BoxBorderG" => 0, "BoxBorderB" => 0, "BoxBackR" => 255, "BoxBackG" => 255, "BoxBackB" => 255, + "RFade" => 206, "GFade" => 133, "BFade" => 30, "ShowLabel" => true]; +$image->drawProgress(40, 60, 77, $progressOptions); + +/* Left Orange bar */ +$progressOptions = ["Width" => 165, "R" => 209, "G" => 125, "B" => 27, "Surrounding" => 20, + "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, "BoxBackR" => 255, "BoxBackG" => 255, + "BoxBackB" => 255, "NoAngle" => true, "ShowLabel" => true, "LabelPos" => LABEL_POS_RIGHT]; +$image->drawProgress(40, 100, 50, $progressOptions); + +/* Left Yellow bar */ +$progressOptions = ["Width" => 165, "R" => 209, "G" => 198, "B" => 27, "Surrounding" => 20, + "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, "BoxBackR" => 255, "BoxBackG" => 255, + "BoxBackB" => 255, "ShowLabel" => true, "LabelPos" => LABEL_POS_LEFT]; +$image->drawProgress(75, 140, 25, $progressOptions); + +/* Left Green bar */ +$progressOptions = ["Width" => 400, "R" => 134, "G" => 209, "B" => 27, "Surrounding" => 20, + "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, "BoxBackR" => 255, "BoxBackG" => 255, + "BoxBackB" => 255, "RFade" => 206, "GFade" => 133, "BFade" => 30, "ShowLabel" => true, + "LabelPos" => LABEL_POS_CENTER]; +$image->drawProgress(40, 180, 80, $progressOptions); + +/* Right vertical Red bar */ +$progressOptions = ["Width" => 20, "Height" => 150, "R" => 209, "G" => 31, "B" => 27, + "Surrounding" => 20, "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, + "BoxBackR" => 255, "BoxBackG" => 255, "BoxBackB" => 255, "RFade" => 206, "GFade" => 133, + "BFade" => 30, "ShowLabel" => true, "Orientation" => ORIENTATION_VERTICAL, "LabelPos" => LABEL_POS_BOTTOM]; +$image->drawProgress(500, 200, 77, $progressOptions); + +/* Right vertical Orange bar */ +$progressOptions = ["Width" => 20, "Height" => 150, "R" => 209, "G" => 125, + "B" => 27, "Surrounding" => 20, "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, + "BoxBackR" => 255, "BoxBackG" => 255, "BoxBackB" => 255, "NoAngle" => true, "ShowLabel" => true, + "Orientation" => ORIENTATION_VERTICAL, "LabelPos" => LABEL_POS_TOP]; +$image->drawProgress(540, 200, 50, $progressOptions); + +/* Right vertical Yellow bar */ +$progressOptions = ["Width" => 20, "Height" => 150, "R" => 209, "G" => 198, + "B" => 27, "Surrounding" => 20, "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, + "BoxBackR" => 255, "BoxBackG" => 255, "BoxBackB" => 255, "ShowLabel" => true, + "Orientation" => ORIENTATION_VERTICAL, "LabelPos" => LABEL_POS_INSIDE]; +$image->drawProgress(580, 200, 25, $progressOptions); + +/* Right vertical Green bar */ +$progressOptions = ["Width" => 20, "Height" => 150, "R" => 134, "G" => 209, + "B" => 27, "Surrounding" => 20, "BoxBorderR" => 0, "BoxBorderG" => 0, "BoxBorderB" => 0, + "BoxBackR" => 255, "BoxBackG" => 255, "BoxBackB" => 255, "RFade" => 206, "GFade" => 133, + "BFade" => 30, "ShowLabel" => true, "Orientation" => ORIENTATION_VERTICAL, "LabelPos" => LABEL_POS_CENTER]; +$image->drawProgress(620, 200, 80, $progressOptions); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawProgressChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/radar.md b/vendor/szymach/c-pchart/resources/doc/radar.md new file mode 100644 index 0000000..ff73cee --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/radar.md @@ -0,0 +1,98 @@ +# Drawing a radar chart + +[Reference](http://wiki.pchart.net/doc.draw.radar.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Radar; +use CpChart\Data; +use CpChart\Image; + +/* Prepare some nice data & axis config */ +$data = new Data(); +$data->addPoints([40, 20, 15, 10, 8, 4], "ScoreA"); +$data->addPoints([8, 10, 12, 20, 30, 15], "ScoreB"); +$data->setSerieDescription("ScoreA", "Application A"); +$data->setSerieDescription("ScoreB", "Application B"); + +/* Create the X serie */ +$data->addPoints(["Size", "Speed", "Reliability", "Functionalities", "Ease of use", "Weight"], "Labels"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw a solid background */ +$settings = ["R" => 179, "G" => 217, "B" => 91, "Dash" => 1, "DashR" => 199, "DashG" => 237, "DashB" => 111]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay some gradient areas */ +$settings = ["StartR" => 194, "StartG" => 231, "StartB" => 44, "EndR" => 43, "EndG" => 107, "EndB" => 58, "Alpha" => 50]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Draw the border */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pRadar - Draw radar charts", ["R" => 255, "G" => 255, + "B" => 255]); + +/* Define general drawing parameters */ +$image->setFontProperties([ + "FontName" => "Forgotte.ttf", + "FontSize" => 10, + "R" => 80, + "G" => 80, + "B" => 80 +]); +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Create the radar object */ +$radarChart = new Radar(); + +/* Draw the 1st radar chart */ +$image->setGraphArea(10, 25, 340, 225); +$Options = ["Layout" => RADAR_LAYOUT_STAR, "BackgroundGradient" => [ + "StartR" => 255, + "StartG" => 255, + "StartB" => 255, + "StartAlpha" => 100, + "EndR" => 207, + "EndG" => 227, + "EndB" => 125, + "EndAlpha" => 50 +]]; +$radarChart->drawRadar($image, $data, $Options); + +/* Draw the 2nd radar chart */ +$image->setGraphArea(350, 25, 690, 225); +$Options = ["Layout" => RADAR_LAYOUT_CIRCLE, "LabelPos" => RADAR_LABELS_HORIZONTAL, "BackgroundGradient" => [ + "StartR" => 255, + "StartG" => 255, + "StartB" => 255, + "StartAlpha" => 50, + "EndR" => 32, + "EndG" => 109, + "EndB" => 174, + "EndAlpha" => 30 +]]; +$radarChart->drawRadar($image, $data, $Options); + +/* Write down the legend */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->drawLegend(270, 205, ["Style" => LEGEND_BOX, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture */ +$image->render("drawRadar.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_best_fit.md b/vendor/szymach/c-pchart/resources/doc/scatter_best_fit.md new file mode 100644 index 0000000..43bd7f7 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_best_fit.md @@ -0,0 +1,108 @@ +# Drawing a scatter best fit chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscatterbestfit.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(rand(1, 20) * 10 + rand(0, $i), "Probe 1"); +} +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(rand(1, 2) * 10 + rand(0, $i), "Probe 2"); +} +$data->setAxisName(0, "X-Index"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_TOP); + +/* Create the Y axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, "Probe 3"); +} +$data->setSerieOnAxis("Probe 3", 1); +$data->setAxisName(1, "Y-Index"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisPosition(1, AXIS_POSITION_LEFT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("Probe 1", "Probe 3", 0); +$data->setScatterSerieDescription(0, "This year"); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); + +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("Probe 2", "Probe 3", 1); +$data->setScatterSerieDescription(1, "Last Year"); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawScatterBestFit() - Linear regression", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 60, 350, 360); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterPlotChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(280, 380, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Draw the line of best fit */ +$myScatter->drawScatterBestFit(); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawScatterBestFit.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_line.md b/vendor/szymach/c-pchart/resources/doc/scatter_line.md new file mode 100644 index 0000000..79b59bb --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_line.md @@ -0,0 +1,100 @@ +# Drawing a scatter line chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscatterlineChart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(cos(deg2rad($i)) * 20, "Probe 1"); +} +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(sin(deg2rad($i)) * 20, "Probe 2"); +} +$data->setAxisName(0, "Index"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + +/* Create the Y axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, "Probe 3"); +} +$data->setSerieOnAxis("Probe 3", 1); +$data->setAxisName(1, "Degree"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisUnit(1, "°"); +$data->setAxisPosition(1, AXIS_POSITION_RIGHT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("Probe 1", "Probe 3", 0); +$data->setScatterSerieDescription(0, "This year"); +$data->setScatterSerieTicks(0, 4); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); + +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("Probe 2", "Probe 3", 1); +$data->setScatterSerieDescription(1, "Last Year"); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$settings = ["StartR" => 219, "StartG" => 231, "StartB" => 139, "EndR" => 1, "EndG" => 138, "EndB" => 68, "Alpha" => 50]; +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawScatterLineChart() - Draw a scatter line chart", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 50, 350, 350); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterLineChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(280, 380, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawScatterLineChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_plot.md b/vendor/szymach/c-pchart/resources/doc/scatter_plot.md new file mode 100644 index 0000000..b8979cf --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_plot.md @@ -0,0 +1,107 @@ +# Drawing a scatter plot chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscatterplotChart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(cos(deg2rad($i)) * 20, "Probe 1"); +} +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(sin(deg2rad($i)) * 20, "Probe 2"); +} +$data->setAxisName(0, "Index"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + +/* Create the Y axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, "Probe 3"); +} +$data->setSerieOnAxis("Probe 3", 1); +$data->setAxisName(1, "Degree"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisUnit(1, "°"); +$data->setAxisPosition(1, AXIS_POSITION_RIGHT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("Probe 1", "Probe 3", 0); +$data->setScatterSerieDescription(0, "This year"); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); + +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("Probe 2", "Probe 3", 1); +$data->setScatterSerieDescription(1, "Last Year"); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawScatterPlotChart() - Draw a scatter plot chart", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 50, 350, 350); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterPlotChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(260, 375, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawScatterPlotChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_spline.md b/vendor/szymach/c-pchart/resources/doc/scatter_spline.md new file mode 100644 index 0000000..2ff0b0c --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_spline.md @@ -0,0 +1,108 @@ +# Drawing a scatter spline chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscattersplineChart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints(rand(1, 30), "Probe 1"); +} +for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints(rand(1, 30), "Probe 2"); +} +$data->setAxisName(0, "Index"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + +/* Create the Y axis and the binded series */ +for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints($i, "Probe 3"); +} +$data->setSerieOnAxis("Probe 3", 1); +$data->setAxisName(1, "Degree"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisUnit(1, "°"); +$data->setAxisPosition(1, AXIS_POSITION_RIGHT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("Probe 1", "Probe 3", 0); +$data->setScatterSerieDescription(0, "This year"); +$data->setScatterSerieTicks(0, 4); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("Probe 2", "Probe 3", 1); +$data->setScatterSerieDescription(1, "Last Year"); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawScatterSplineChart() - Draw a scatter spline chart", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 50, 350, 350); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterSplineChart(); +$myScatter->drawScatterPlotChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(280, 380, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawScatterSplineChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_threshold.md b/vendor/szymach/c-pchart/resources/doc/scatter_threshold.md new file mode 100644 index 0000000..6d43c19 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_threshold.md @@ -0,0 +1,116 @@ +# Drawing a scatter threshold chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscatterthreshold.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +$data->createFunctionSerie("X", "1/z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setAxisName(0, "x = 1/z"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + +/* Create the Y axis */ +$data->createFunctionSerie("Y", "z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setSerieOnAxis("Y", 1); +$data->setAxisName(1, "y = z"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisPosition(1, AXIS_POSITION_RIGHT); + +/* Create the Y axis */ +$data->createFunctionSerie("Y2", "z*z*z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setSerieOnAxis("Y2", 2); +$data->setAxisName(2, "y = z*z*z"); +$data->setAxisXY(2, AXIS_Y); +$data->setAxisPosition(2, AXIS_POSITION_LEFT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("X", "Y", 0); +$data->setScatterSerieDescription(0, "Pass A"); +$data->setScatterSerieTicks(0, 4); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); + +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("X", "Y2", 1); +$data->setScatterSerieDescription(1, "Pass B"); +$data->setScatterSerieTicks(1, 4); +$data->setScatterSerieColor(1, ["R" => 120, "G" => 0, "B" => 255]); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "createFunctionSerie() - Functions computing", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 50, 350, 350); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(["XMargin" => 10, "YMargin" => 10, "Floating" => true]); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the 0/0 lines */ +$myScatter->drawScatterThreshold(0, ["AxisID" => 0, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 10]); +$myScatter->drawScatterThreshold(0, ["AxisID" => 1, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 10]); + +/* Draw a treshold area */ +$myScatter->drawScatterThresholdArea(-0.1, 0.1, ["AreaName" => "Error zone"]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterLineChart(); +$myScatter->drawScatterPlotChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(300, 380, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.createFunctionSerie.scatter.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/scatter_threshold_area.md b/vendor/szymach/c-pchart/resources/doc/scatter_threshold_area.md new file mode 100644 index 0000000..5f85a7d --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/scatter_threshold_area.md @@ -0,0 +1,116 @@ +# Drawing a scatter threshold area chart + +[Reference](http://wiki.pchart.net/doc.scatter.drawscatterthresholdarea.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Scatter; +use CpChart\Data; +use CpChart\Image; + +/* Create the Data object */ +$data = new Data(); + +/* Create the X axis and the binded series */ +$data->createFunctionSerie("X", "1/z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setAxisName(0, "x = 1/z"); +$data->setAxisXY(0, AXIS_X); +$data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + +/* Create the Y axis */ +$data->createFunctionSerie("Y", "z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setSerieOnAxis("Y", 1); +$data->setAxisName(1, "y = z"); +$data->setAxisXY(1, AXIS_Y); +$data->setAxisPosition(1, AXIS_POSITION_RIGHT); + +/* Create the Y axis */ +$data->createFunctionSerie("Y2", "z*z*z", ["MinX" => -10, "MaxX" => 10, "XStep" => 1]); +$data->setSerieOnAxis("Y2", 2); +$data->setAxisName(2, "y = z*z*z"); +$data->setAxisXY(2, AXIS_Y); +$data->setAxisPosition(2, AXIS_POSITION_LEFT); + +/* Create the 1st scatter chart binding */ +$data->setScatterSerie("X", "Y", 0); +$data->setScatterSerieDescription(0, "Pass A"); +$data->setScatterSerieTicks(0, 4); +$data->setScatterSerieColor(0, ["R" => 0, "G" => 0, "B" => 0]); + +/* Create the 2nd scatter chart binding */ +$data->setScatterSerie("X", "Y2", 1); +$data->setScatterSerieDescription(1, "Pass B"); +$data->setScatterSerieTicks(1, 4); +$data->setScatterSerieColor(1, ["R" => 120, "G" => 0, "B" => 255]); + +/* Create the Image object */ +$image = new Image(400, 400, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +/* Overlay with a gradient */ +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "createFunctionSerie() - Functions computing", [ + "R" => 255, + "G" => 255, + "B" => 255 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Set the graph area */ +$image->setGraphArea(50, 50, 350, 350); + +/* Create the Scatter chart object */ +$myScatter = new Scatter($image, $data); + +/* Draw the scale */ +$myScatter->drawScatterScale(["XMargin" => 10, "YMargin" => 10, "Floating" => true]); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw the 0/0 lines */ +$myScatter->drawScatterThreshold(0, ["AxisID" => 0, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 10]); +$myScatter->drawScatterThreshold(0, ["AxisID" => 1, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 10]); + +/* Draw a treshold area */ +$myScatter->drawScatterThresholdArea(-0.1, 0.1, ["AreaName" => "Error zone"]); + +/* Draw a scatter plot chart */ +$myScatter->drawScatterLineChart(); +$myScatter->drawScatterPlotChart(); + +/* Draw the legend */ +$myScatter->drawScatterLegend(300, 380, ["Mode" => LEGEND_HORIZONTAL, "Style" => LEGEND_NOBORDER]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.createFunctionSerie.scatter.png"); + +``` diff --git a/vendor/szymach/c-pchart/resources/doc/spline.md b/vendor/szymach/c-pchart/resources/doc/spline.md new file mode 100644 index 0000000..b80cbe2 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/spline.md @@ -0,0 +1,37 @@ +# Drawing a spline chart + +[Reference](http://wiki.pchart.net/doc.chart.drawsplinechart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +// Create and populate data +$data = new Data(); +$data->addPoints([], "Serie1"); + +// Create the image and set the data +$image = new Image(700, 230, $data); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]); + +// 1st spline drawn in white with control points visible +$firstCoordinates = [[40, 80], [280, 60], [340, 166], [590, 120]]; +$fistSplineSettings = ["R" => 255, "G" => 255, "B" => 255, "ShowControl" => true]; +$image->drawSpline($firstCoordinates, $fistSplineSettings); + +// 2nd spline dashed drawn in white with control points visible +$secondCoordinates = [[250, 50], [250, 180], [350, 180], [350, 50]]; +$secondSplineSettings = [ + "R" => 255, + "G" => 255, + "B"=> 255, + "ShowControl" => true, + "Ticks" => 4 +]; +$image->drawSpline($secondCoordinates, $secondSplineSettings); + +// Render the picture (choose the best way) +$image->autoOutput("example.drawSpline.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/split_path.md b/vendor/szymach/c-pchart/resources/doc/split_path.md new file mode 100644 index 0000000..103a91e --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/split_path.md @@ -0,0 +1,74 @@ +# Drawing a split path chart + +[Reference](http://wiki.pchart.net/doc.chart.drawsplitpath.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; +use CpChart\Chart\Split; + +/* Create the Image object */ +$data = new Image(700, 230); + +/* Draw the background */ +$settings = [ + "R" => 170, + "G" => 183, + "B" => 87, + "Dash" => 1, + "DashR" => 190, + "DashG" => 203, + "DashB" => 107 +]; +$data->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = ["StartR" => 219, "StartG" => 231, "StartB" => 139, "EndR" => 1, + "EndG" => 138, "EndB" => 68, "Alpha" => 50]; +$data->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$data->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, ["StartR" => 0, "StartG" => 0, + "StartB" => 0, "EndR" => 50, "EndG" => 50, "EndB" => 50, "Alpha" => 80]); + +/* Add a border to the picture */ +$data->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$data->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$data->drawText(10, 13, "pSplit - Draw splitted path charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Set the default font properties */ +$data->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 10, "R" => 80, "G" => 80, "B" => 80]); + +/* Enable shadow computing */ +$data->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([30, 20, 15, 10, 8, 4], "Score"); +$data->addPoints(["End of visit", "Home Page", "Product Page", "Sales", "Statistics", "Prints"], "Labels"); +$data->setAbscissa("Labels"); + +/* Create the pSplit object */ +$splitChart = new Split(); + +/* Draw the split chart */ +$settings = ["TextPos" => TEXT_POS_RIGHT, "TextPadding" => 10, "Spacing" => 20, "Surrounding" => 40]; +$data->setGraphArea(10, 20, 340, 230); +$splitChart->drawSplitPath($data, $data, $settings); + +/* Create and populate the Data object */ +$data2 = new Data(); +$data2->addPoints([30, 20, 15], "Score"); +$data2->addPoints(["UK", "FR", "ES"], "Labels"); +$data2->setAbscissa("Labels"); + +/* Draw the split chart */ +$settings = ["TextPadding" => 4, "Spacing" => 30, "Surrounding" => 20]; +$data->setGraphArea(350, 50, 690, 200); +$splitChart->drawSplitPath($data, $data2, $settings); + +/* Render the picture (choose the best way) */ +$data->autoOutput("example.split.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/spring.md b/vendor/szymach/c-pchart/resources/doc/spring.md new file mode 100644 index 0000000..230e08d --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/spring.md @@ -0,0 +1,62 @@ +# Drawing a spring chart + +[Reference](http://wiki.pchart.net/doc.spring.drawspring.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Spring; +use CpChart\Image; + +/* Create the Image object */ +$image = new Image(300, 300); + +/* Background customization */ +$image->drawGradientArea(0, 0, 300, 300, DIRECTION_HORIZONTAL, [ + "StartR" => 217, + "StartG" => 250, + "StartB" => 116, + "EndR" => 181, + "EndG" => 209, + "EndB" => 27, + "Alpha" => 100 +]); +$image->drawGradientArea(0, 0, 300, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); +$image->drawRectangle(0, 0, 299, 299, ["R" => 0, "G" => 0, "B" => 0]); +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pSpring - Draw spring charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Prepare the graph area */ +$image->setGraphArea(20, 20, 280, 280); +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 9, "R" => 80, "G" => 80, "B" => 80]); +$image->setShadow(true, ["X" => 2, "Y" => 2, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Create the pSpring object */ +$springChart = new Spring(); + +/* Set the nodes default settings */ +$springChart->setNodeDefaults(["FreeZone" => 50]); + +/* Build random nodes & connections */ +for ($i = 0; $i <= 10; $i++) { + $connections = []; + for ($j = 0; $j <= rand(0, 1); $j++) { + $connections[] = rand(0, 10); + } + $springChart->addNode($i, ["Name" => "Node " . $i, "Connections" => $connections]); +} + +/* Compute and draw the Spring Graph */ +$springChart->drawSpring($image, ["DrawQuietZone" => true]); + +/* Render the picture */ +$image->render("drawSpring3.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/stacked_area.md b/vendor/szymach/c-pchart/resources/doc/stacked_area.md new file mode 100644 index 0000000..55138ff --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/stacked_area.md @@ -0,0 +1,88 @@ +# Drawing a stacked area chart + +[Reference](http://wiki.pchart.net/doc.chart.drawstackedareachart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([1, -2, -1, 2, 1, 0], "Probe 1"); +$data->addPoints([1, -2, -3, 2, 1, 8], "Probe 2"); +$data->addPoints([2, 4, 2, 0, 4, 2], "Probe 3"); +$data->setSerieTicks("Probe 2", 4); +$data->setAxisName(0, "Temperatures"); +$data->addPoints(["Jan", "Feb", "Mar", "Apr", "May", "Jun"], "Labels"); +$data->setSerieDescription("Labels", "Months"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawStackedAreaChart() - draw a stacked area chart", ["R" => 255, "G" => 255, "B" => 255]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(250, 55, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Draw the scale and the 1st chart */ +$image->setGraphArea(60, 60, 450, 190); +$image->drawFilledRectangle(60, 60, 450, 190, ["R" => 255, "G" => 255, "B" => 255, + "Surrounding" => -200, "Alpha" => 10]); +$image->drawScale(["DrawSubTicks" => true, "Mode" => SCALE_MODE_ADDALL]); +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->setShadow(false); +$image->drawStackedAreaChart(["DisplayValues" => true, "DisplayColor" => DISPLAY_AUTO, "Surrounding" => 20]); + +/* Draw the scale and the 2nd chart */ +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "Mode" => SCALE_MODE_ADDALL, "DrawSubTicks" => true]); +$image->setShadow(false); +$image->drawStackedAreaChart(["Surrounding" => 10]); + +/* Write the chart legend */ +$image->drawLegend(510, 205, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawStackedAreaChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/stacked_bar.md b/vendor/szymach/c-pchart/resources/doc/stacked_bar.md new file mode 100644 index 0000000..a06bd1b --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/stacked_bar.md @@ -0,0 +1,87 @@ +# Drawing a stacked bar chart + +[Reference](http://wiki.pchart.net/doc.chart.drawstackedbarchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([-7, -8, -15, -20, -18, -12, 8, -19, 9, 16, -20, 8, 10, -10, -14, -20, 8, -9, -19], "Probe 3"); +$data->addPoints([19, 0, -8, 8, -8, 12, -19, -10, 5, 12, -20, -8, 10, -11, -12, 8, -17, -14, 0], "Probe 4"); +$data->setAxisName(0, "Temperatures"); +$data->addPoints([4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], "Time"); +$data->setSerieDescription("Time", "Hour of the day"); +$data->setAbscissa("Time"); +$data->setXAxisUnit("h"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = [ + "R" => 170, + "G" => 183, + "B" => 87, + "Dash" => 1, + "DashR" => 190, + "DashG" => 203, + "DashB" => 107 +]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, + "EndB" => 68, + "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + +/* Set the default font properties */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Draw the scale */ +$image->setGraphArea(60, 30, 650, 190); +$image->drawScale([ + "CycleBackground" => true, + "DrawSubTicks" => true, + "GridR" => 0, + "GridG" => 0, + "GridB" => 0, + "GridAlpha" => 10, + "Mode" => SCALE_MODE_ADDALL +]); + +/* Turn on shadow computing */ +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); + +/* Draw some thresholds */ +$image->setShadow(false); +$image->drawThreshold(-40, ["WriteCaption" => true, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 4]); +$image->drawThreshold(28, ["WriteCaption" => true, "R" => 0, "G" => 0, "B" => 0, "Ticks" => 4]); + +/* Draw the chart */ +$image->drawStackedBarChart([ + "Rounded" => true, + "DisplayValues" => true, + "DisplayColor" => DISPLAY_AUTO, + "DisplaySize" => 6, + "BorderR" => 255, + "BorderG" => 255, + "BorderB" => 255 +]); + +/* Write the chart legend */ +$image->drawLegend(570, 212, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawStackedBarChart.rounded.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/step.md b/vendor/szymach/c-pchart/resources/doc/step.md new file mode 100644 index 0000000..ef094b9 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/step.md @@ -0,0 +1,93 @@ +# Drawing a step chart + +[Reference](http://wiki.pchart.net/doc.chart.drawstepchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([-4, VOID, VOID, 12, 8, 3], "Probe 1"); +$data->addPoints([3, 12, 15, 8, 5, -5], "Probe 2"); +$data->addPoints([2, 7, 5, 18, 19, 22], "Probe 3"); +$data->setSerieTicks("Probe 2", 4); +$data->setAxisName(0, "Temperatures"); +$data->addPoints(["Jan", "Feb", "Mar", "Apr", "May", "Jun"], "Labels"); +$data->setSerieDescription("Labels", "Months"); +$data->setAbscissa("Labels"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = [ + "StartR" => 219, + "StartG" => 231, + "StartB" => 139, + "EndR" => 1, + "EndG" => 138, "EndB" => 68, "Alpha" => 50 +]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); +$image->drawGradientArea(0, 0, 700, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 80 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "drawStepChart() - draw a step chart", ["R" => 255, "G" => 255, "B" => 255]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(250, 55, "Average temperature", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Draw the scale and the 1st chart */ +$image->setGraphArea(60, 60, 450, 190); +$image->drawFilledRectangle(60, 60, 450, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["DrawSubTicks" => true]); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$image->drawStepChart(["DisplayValues" => true, "DisplayColor" => DISPLAY_AUTO]); +$image->setShadow(false); + +/* Draw the scale and the 2nd chart */ +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "DrawSubTicks" => true]); +$image->setShadow(true, ["X" => -1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 10]); +$image->drawStepChart(); +$image->setShadow(false); + +/* Write the chart legend */ +$image->drawLegend(510, 205, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawStepChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/stock.md b/vendor/szymach/c-pchart/resources/doc/stock.md new file mode 100644 index 0000000..fd4378a --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/stock.md @@ -0,0 +1,78 @@ +# Drawing a stock chart + +[Reference](http://wiki.pchart.net/doc.stocks.drawstockchart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Stock; +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +$data->addPoints([34, 55, 15, 62, 38, 42], "Open"); +$data->addPoints([42, 25, 40, 38, 49, 36], "Close"); +$data->addPoints([27, 14, 12, 25, 32, 32], "Min"); +$data->addPoints([45, 59, 47, 65, 64, 48], "Max"); +$data->setAxisDisplay(0, AXIS_FORMAT_CURRENCY, "$"); +$data->addPoints(["8h", "10h", "12h", "14h", "16h", "18h"], "Time"); +$data->setAbscissa("Time"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Draw the background */ +$settings = ["R" => 170, "G" => 183, "B" => 87, "Dash" => 1, "DashR" => 190, "DashG" => 203, "DashB" => 107]; +$image->drawFilledRectangle(0, 0, 700, 230, $settings); + +/* Overlay with a gradient */ +$settings = ["StartR" => 219, "StartG" => 231, "StartB" => 139, "EndR" => 1, "EndG" => 138, "EndB" => 68, "Alpha" => 50]; +$image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + +/* Draw the border */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(60, 45, "Stock price", ["FontSize" => 28, "Align" => TEXT_ALIGN_BOTTOMLEFT]); + +/* Draw the 1st scale */ +$image->setGraphArea(60, 60, 450, 190); +$image->drawFilledRectangle(60, 60, 450, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["DrawSubTicks" => true, "CycleBackground" => true]); + +/* Draw the 1st stock chart */ +$mystockChart = new Stock($image, $data); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]); +$mystockChart->drawStockChart(); + +/* Reset the display mode because of the graph small size */ +$data->setAxisDisplay(0, AXIS_FORMAT_DEFAULT); + +/* Draw the 2nd scale */ +$image->setShadow(false); +$image->setGraphArea(500, 60, 670, 190); +$image->drawFilledRectangle(500, 60, 670, 190, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 10 +]); +$image->drawScale(["Pos" => SCALE_POS_TOPBOTTOM, "DrawSubTicks" => true]); + +/* Draw the 2nd stock chart */ +$mystockChart = new Stock($image, $data); +$image->setShadow(true, ["X" => 1, "Y" => 1, "R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]); +$mystockChart->drawStockChart(); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawStockChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/surface.md b/vendor/szymach/c-pchart/resources/doc/surface.md new file mode 100644 index 0000000..5e544ad7 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/surface.md @@ -0,0 +1,81 @@ +# Drawing a surface chart + +[Reference](http://wiki.pchart.net/doc.surface.drawsurface.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Chart\Surface; +use CpChart\Data; +use CpChart\Image; + +/* Create the Image object */ +$image = new Image(400, 400); + +/* Create a solid background */ +$settings = ["R" => 179, "G" => 217, "B" => 91, "Dash" => 1, "DashR" => 199, "DashG" => 237, "DashB" => 111]; +$image->drawFilledRectangle(0, 0, 400, 400, $settings); + +$image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, [ + "StartR" => 194, + "StartG" => 231, + "StartB" => 44, + "EndR" => 43, + "EndG" => 107, + "EndB" => 58, + "Alpha" => 50 +]); +$image->drawGradientArea(0, 0, 400, 20, DIRECTION_VERTICAL, [ + "StartR" => 0, + "StartG" => 0, + "StartB" => 0, + "EndR" => 50, + "EndG" => 50, + "EndB" => 50, + "Alpha" => 100 +]); + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 399, 399, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the picture title */ +$image->setFontProperties(["FontName" => "Silkscreen.ttf", "FontSize" => 6]); +$image->drawText(10, 13, "pSurface() :: 2D surface charts", ["R" => 255, "G" => 255, "B" => 255]); + +/* Define the charting area */ +$image->setGraphArea(20, 40, 380, 380); +$image->drawFilledRectangle(20, 40, 380, 380, [ + "R" => 255, + "G" => 255, + "B" => 255, + "Surrounding" => -200, + "Alpha" => 20 +]); + +$image->setShadow(true, ["X" => 1, "Y" => 1]); + +/* Create the surface object */ +$surfaceChart = new Surface($image); + +/* Set the grid size */ +$surfaceChart->setGrid(20, 20); + +/* Write the axis labels */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); +$surfaceChart->writeXLabels(); +$surfaceChart->writeYLabels(); + +/* Add random values */ +for ($i = 0; $i <= 50; $i++) { + $surfaceChart->addPoint(rand(0, 20), rand(0, 20), rand(0, 100)); +} + +/* Compute the missing points */ +$surfaceChart->computeMissing(); + +/* Draw the surface chart */ +$surfaceChart->drawSurface(["Border" => true, "Surrounding" => 40]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.surface.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/doc/zone.md b/vendor/szymach/c-pchart/resources/doc/zone.md new file mode 100644 index 0000000..2d12b2b --- /dev/null +++ b/vendor/szymach/c-pchart/resources/doc/zone.md @@ -0,0 +1,71 @@ +# Drawing a zone chart + +[Reference](http://wiki.pchart.net/doc.chart.drawzonechart.html) + +```php +require '/path/to/your/vendor/autoload.php'; + +use CpChart\Data; +use CpChart\Image; + +/* Create and populate the Data object */ +$data = new Data(); +for ($i = 0; $i <= 10; $i = $i + .2) { + $data->addPoints(log($i + 1) * 10, "Bounds 1"); + $data->addPoints(log($i + 3) * 10 + rand(0, 2) - 1, "Probe"); + $data->addPoints(log($i + 6) * 10, "Bounds 2"); + $data->addPoints($i * 10, "Labels"); +} +$data->setAxisName(0, "Size (cm)"); +$data->setSerieDescription("Labels", "Months"); +$data->setAbscissa("Labels"); +$data->setAbscissaName("Time (years)"); + +/* Create the Image object */ +$image = new Image(700, 230, $data); + +/* Turn off Antialiasing */ +$image->Antialias = false; + +/* Add a border to the picture */ +$image->drawRectangle(0, 0, 699, 229, ["R" => 0, "G" => 0, "B" => 0]); + +/* Write the chart title */ +$image->setFontProperties(["FontName" => "Forgotte.ttf", "FontSize" => 11]); +$image->drawText(150, 35, "Size by time generations", ["FontSize" => 20, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]); + +/* Set the default font */ +$image->setFontProperties(["FontName" => "pf_arma_five.ttf", "FontSize" => 6]); + +/* Define the chart area */ +$image->setGraphArea(40, 40, 680, 200); + +/* Draw the scale */ +$image->drawScale([ + "LabelSkip" => 4, + "XMargin" => 10, + "YMargin" => 10, + "Floating" => true, + "GridR" => 200, + "GridG" => 200, + "GridB" => 200, + "DrawSubTicks" => true, + "CycleBackground" => true +]); + +/* Turn on Antialiasing */ +$image->Antialias = true; + +/* Draw the line chart */ +$image->drawZoneChart("Bounds 1", "Bounds 2"); +$data->setSerieDrawable(["Bounds 1", "Bounds 2"], false); + +/* Draw the line chart */ +$image->drawStepChart(); + +/* Write the chart legend */ +$image->drawLegend(640, 20, ["Style" => LEGEND_NOBORDER, "Mode" => LEGEND_HORIZONTAL]); + +/* Render the picture (choose the best way) */ +$image->autoOutput("example.drawZoneChart.png"); +``` diff --git a/vendor/szymach/c-pchart/resources/fonts/Bedizen.ttf b/vendor/szymach/c-pchart/resources/fonts/Bedizen.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d115733305c5cfd7772b20a911692b39ab376add GIT binary patch literal 42912 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%0Dj|rDgUqq!3r&C+Tmv5%q+6R|$8M=}Dj@(br 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 literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/Forgotte.ttf b/vendor/szymach/c-pchart/resources/fonts/Forgotte.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a2f7f4ab11f041a6d72e99a8f95c6b6b00a1b27d GIT binary patch literal 42148 zcmeFacYIvcnLd8*ou-*l?_Dz*^)f1>VpU6WZ*m3O7$l8kwcskr1$SJ)y_*mNHX$}F z8z+G0!tRM3A>xH*-dsynE5@=duJq>u;0(`kNxZW`Hp?xd+xdC zzUO_P^PY3wQ|^`0lv42^X62f{Z0YQew;r3Olw}HHuP=k#myzb(zP0`hrcz6jQVFrRKHRr?%%!%PURNr84brC%Y#3ZyJ!`Z_snq+y zqj%6hxJ^xxev&Ht&=O#jRq&u_fh6FTn={x3rhXXrP8etHJE~fJtSq{+SP`o$^B$!t5W=&@Z+`odcGad_?p(by*sI+78(JRj zNDkU#dMCmz9Q%wzdr%eXn^dm$oGMY9Rk1c*xilT6wpr!j+^FtBTq7*z>#9_JU!`bY zQbpQjm9O2Uk`XS}9>vj*XZ{B9kEjB*AIDBrpuM2tG?yyXzNbone!BK$q`ON+YY!a+$#qT<&d*XyBGQNs0{5J;JZcH^v^@C zgUX{FL)?(c)-qIrdSB&ee})WCK)#bINjrk)Zh<_9aQ|M!zpDzhv&yZVS7qu5u3u9z z+U*FRQDyog;LGC@(j3A0DCDlj`61;|Z$RfS;TTX({SFnY5uY=_Z3}puQswF}&Oyl6 zrLxo~Do5X@O7u)st*ukJ>M@n7E^bnF`0A#lPDa~>Rj;8ID&%5 z<4E`EgEAAB$;VceC>RijPaXOp;7nYA$>ak#IB+=Cnc#on`13d(56a^st>@Y z3YPr=;x|LDeX3UdBg*yyYRregdJD?;oNCmcR*h(pR^eV6@Qc;{fJ(7R?G*ca0rr7w zbrfZ#p^W&0-HW}j>}XFYTd^IKBc6{DJE6^7f~~{ufWdFIg5YoQFOGG#+ zbe%FDpC+M%@$1QfWKFw)1Mj72$%vbHescO7!_4oqew1Mn+tf)HDY2jR0OFueERO5; zNKLcY%$iBpbj@Ve&HR}xW=%7j%w~&RA%H7TT${`$D-xJ=3z8xVR5O`0n&B&VxfQ)l28rA zLmwk-7JW=sLss2vAq)|?i4;i2yO6_dil9ItnGJo2x9~Se=$=49c|-S*8<^?V2v`Bj z)MSEu$i&2g)Ga2>91#tDY)GXW`WW2t4D^YNML~x3fozhM*=hlIC}1@%sSlLnHFBfu zCg_8cg^8$-oe9l^AcESD>m$#H`AoB+T{#cU*w=FP0 zB?1cvWNU`k5)Xas02`$PLxp(E-D*WF6fpEb)VMx~qU! zLg0cwgOA8;8oCG)|GmV34g6qUDbk7O)$YA`QTv~NI^7FDp*`3m-QJ;v&2Q4tZMk1eE6s@Z0T zK6WTTeGFvq4D?Az`?NmfhUd`)+abS{5iTLET{4QWMIjQ^-9!}#0QEtRguzU0!5AfN ziE@6HK1>(-g;WDO{HSaAhFm)$*)fSe_9#2UPN0MCK#zz@gFcaVyXeEjL&R$!v}i-1PprdX=z|QXPqxMGjCMGjP(VO{Ny#(NCnd*Zg8pHBERt7* z!+}_v6Z-HUFJXjEBW$NWHcRA<`rtXc&0!I3AsqCHi9-2EKGa9@gV0nY^b6gK8P~^% z7hP}A#~JH%z@>=MVmF!O#9;lCX3w+1nmy$18u>WE=o)kj3gv8D=+H+8eQY(#7_iwIAQ@8@gKh@ zDa=tZ?6;*0bH+IxD5@wc^|6{9aW0EJGREnQWce_)a0d_QgCD$79;RoEITDqLD~yk{ z50q8=d)#wj&OlSos&A^VV!p+M*&2&_SUsYw%BCVPyXC-WITEAvXcdDoRUF2r2`W(~ zsbtKeq^dNGbu+*-OJ%DZ@N#2}?7`SKA7j`;jCG4K?{XP)E0-__QLelg@m8uT%<=dz zFZ30RdB3jeRK1#_8ZZ~rq?%QWYE@G)ztgTd)HF3+%}_JdEHzurQFGNiHD4`Ioob<4 zq!z0s>KV0EeM4P^4!x>R^{YX(PK~OqYP;H|cBoyLr`fIU#E5vW+OH0%yVYIlpgM$^ zjH8$tIi^mipDTuSV48)xDUlc~U*7zKB_pi<06TX2$*wp}$doE7v5CV;)7R z9yO%8)vy{+x2rX3EoSL9sEvqUj~S~iY7^$G&Z`HQ9KZY3t#ZCknYAAwbSrSPpe%}k z(PC(H8*tbKNiIW|FQarH2inh|q<^TctDmX2)GyWV)bG_F)n8OltI|5OrJ7&cs_oKF z>#yoR*WYp_y3$?Qu3T4vtI;*xb;X?<3?dg7N^~L6Uj_ZPL#xZsVlwv^!2LDMC%vuS z0r!v8f2mKvy_MaDPR(C%95wnZmtcf_v~SrGh`l!5K#td?E-;f?p2)ZSaxc zrQk=w3%Gh9h}qZRc^qE|-V54~V@A*)bO&b!-JksAlb1gE*1Jfj)Vq7$-3dDSZrVF{ z!@`Lb=W*BJ*Z?&D`u91B8UW=%t77T@^=}!hgFk~Z^jY=CuzJJB!8VnE1uA_<13%hf(M>a)3d&T zHrLeMRgU(#%;lOfJpFO42N!x7B1+r{naW%u9>bO|LVuh z`EtgM{=+LPT#u`zD~3Urt#CiywlZTp*uQdRBlwuf2ah26;g#UzSxZx+pQe8wcF4|lcyV9ty`Hkwp-Ajy3u30mA(<^fo(q6=2zfG zBl4PyL*Ixzh7q^}#~~bF!0{-KZ{YYIjyG}q2FHKnkUZ=Nq~R#WVFFt_*jBs+JPP3z zPz|=-Yul8YnwE>DJoqF^UyhP7!GFny4b-b6SK+)kL~t{r zs6aIeisu~MHWYKHoVfUgtFR3t^o+`Z5)E(B%qpHyQ59F=#pFujXjlUuDuRzg6mpSJ zgVxbF6Acs6dl9xGY(=;T;S7W`Du9N`lVAf7xdkm+O_NrgoMfxPRhE{RjH_CWw{Ey& zOG=a*&;O+ACl~b2$`a4Qs*=2-3Rh)fx~oz>i5+Z}0T?)2OQ+u|i{U&?#6 zwxqetmYA2t`caJamTtWl_UDA{XpmT;+!Jh`+V0GBz4v)GM=tfCR-(NQ$d_Ifr<-Qv zghC36(cBye>j>)=As!CcA6G^-F;dfbh(5a!7a0m6FDP7}VoR|VCV6UYh4qE?CyJM4 z4|nCvEuNLr)t{{muUkJWZPnuRsq5FZq%XQ99h^0I_y^U$U>(Y-rm^(VQ9&j)EH=nt zgG@TYc7*MeDS{D@$th(Bp>dNXMJ22#m3j~I_Mo~0`+r$le$(>u#s9f>ZT;}D_9w|w)Pl&29M<@c~=NgUab(pJc~*7v;=Cw|`{e2S57aGVc;(}6Y9 z47Rpv&5|Y_%u4kIp}d$z3)Mrd^gr|dWYiZZT#9su&~C(_iKsWM+J(esSS(utCzZo> z0O7=JwTr$*&|@#2$yI&NC=VoNrSU-GaMPrqL7r$}G+Q*$bX+?S&tiV{RfR+|`bbo(Z zcGZERsoKzvx@KoiZOO8!?d{JV&&padr6kRHM~%<#s>q3*$8ygH*$w$i)E2ZikfRKT z9kPYxXB$)mxrz{P$Vzcg0McZzu`NXEIHZWktBpEHQTn6}W*5aCO!_Kn=Be{(i8b{lg$wWKpV~CFzCXP%zp~tmjw@%(+oo;P zI~$v4RaRRP7Ixg#(P3%zdTZ+2D}CiP)Aj?+Wav2rJsm)F6+Hwe#4zM#XTpjU)|fiP zuqTpgh+5~6+AvY;*jzZTUxjA6vPlaO6$({WM}Q-AqvZ|%k_Yu2n; zq@51js%;J&Cho-`cS4?YwUk{Id=(rSU>t7yV`v}Oaqu4E6LD?D!OYVUpH4e5g&ZHw zA`sPBC?q)b#hYd4Z2EG`8KfU`E^iY1LKbjoP&KZ?)CW zI^KcGu8VvJAzuzGW8s8l&>E7FdorYAIa?7fLtGBRQ3yvNp7png^)(+e8k7npE@vBB zVyU+*S#-jNpCWcE71%Ij3!D@~dXF~4mESqW{SZRMg-l}RDUb5|1+A`>2L94;Gh!vaC7`}n0 zR$F#`&hu~Tou53Z$F|}Q>odzG1=9QI%M^#)4;!XeXfZZuXY>^)6NF=+bqv@t1H%~N z@kFuIj$+(5T#;u~CBnH#n+t@!6sO4mDg z*7Q4_tD8I8+B(`+MMw76?rbkdLx)DYDiH9N*ERU6D(e6mY@z_z>;g7vaOalO{WZLf zd}tF1%_4D~1v{YC79fsocW%f==$J(!E)Dn6CR=U{Y5+BMOWTg#L@c*O^N7nPZ5tYO zm`iO!NVD1+&*~Y|i%K7AZfNRQ)zWrn@1}#UlJxYyow=+f;F)FZQRA9%bz>ZOY9isv}Mg?|^ z3efKxqXIiBoE<=*ni-=4J4OX|85OYE2v^uBgxNov*?F@WandPC^yJo}o2oX21IbCN z($ixi(@be;m}QL83Oog|v4NlEdg9_@V?vk*|3%#*a!0~e3}%kW%wjZj*psxE3)Z7XVq4al+SH%obiJp|wZ9yz6VrwOUKImXv5K(TW4V(6%`3NvmfaIXdGBORO^qO|A~hP})Y= zTLxO8RnI8;2W}kn5nKQ`Y;#Ur(?^KMnKl-#act8wuN!e3CS|cOGNlDb5*<)saYn&N z>u80$ngw-g#=9IFx|{^)_TQ~=7}9c@Z*~{u7R;WjP1SZbw6y4*vu1i`&rHZ}uGmsq z;j5pv|C1*dKtH4H+>H^d8?6cYnye6HiD)5c>XCqiEeU0%!LSu!J>hi;TLm|`FkwWf zRS5T?37G%ogGqb){dLw`GVIR6usbU}?9RflI}5|^ zEDXD|Fzn94usaLG?ko(uvoP$=!mv9lJnYUw3zmh3Jxhk&iPupCY?9JLt_r8)34d$| zek;zgVze+6jH_+5TC~A&<0ENPcu4&(oinF2wN)*iakXVrS&heY>Ud6ZnNRQR>};A7 z5iz%A-h92Y{X11vmA=5o-zhDxs|Pl$D@t1rY!Xz)RRF{a25}OI8^qw4!$8?^b7ioV z6lY_buigIW;#u<-J^HnqwIa{6%a&^qi|0;V9Qe@Ttwh-n^X=ekM=t^&I{|1XhU!lO zkdA=j99V?wPc&^WQtHG+u2flk?6{OTU$ywv?{&_*Y4IZscidK5sdp|}6!_DU#n38H zvr5Y#4E-0QHQ{;)bu8B4u3!R#ht?aorcL?t z_FaD_X6>&4ju3Bv2%P{87gEUE3T^1cX{_OFe}fTtvd`a%U@|?O6d=O!HvRS#@HLuoTqA6Z$Bfn{8uu9&Pt-8#k^GFZx(ZGS zqK_Nh7>6dIGGmc(n5NzF^1P143!nPl!u*`myS_QUQ*+Oo-QF4aae3Y=Xg$Mvvfeq> zEVK-ehn`iq#b8s;QllTju7-Z01xLj7P&>~qhXV}@urLD+VhfK{{$U6cSKji|rly~w zdVZ`~KY3EK1^zpfr=pK+QVBPVe~fV+{8`^nQxhQrW^o`>5sp+ImNC|sLuD*hq>bZ& zg)``QLWLrgsOA4E-gOlGL(x;@O{0cr7)po z>1|ET_fOZ_qH`NcuRMNJ00tO%4Yn07mn6t3V?=JhMqMR7^x0?wYz+ysmh{gJ*fagf ze;^5-)V?cNbsObK(P&>^jqp%)hkjbv*p>84rj zyGCipBbTsOP0J2DQnUb;Gpvdh%^Gc#qS1oDUrd6s^o%`hgy9c+1{?qW?!!k9FPQiK z`+Dbzj(fGDUoV@shkch+z}_DBThs|oQRG%5glZ_}7orAK}82buWMWd7sLwnEGy`>tg*Hl@!`);=g& zxUh)t6BPvB(ta4katipf6*T%9%?;c9VPLuTXy7e64jSHV_@TO>7l-69^5A@UVf_Wu z0h9K}!WugEI$A5G4PcBC4>V63vrkzNh&=#jo5DQ+D+0&y^c6YAN;O)RRJ1IqXjxLx zvZSJANkz+&ik2l6ElVm|mQ=JXso|C-6)j6DT9#DQa(M2w2+Y8tv&x5^+!$61sq$=- z2^<_J*6r{tAXaX|3s_qJ8+9Gio6fomzH()DYwLq)S+3-^=H|A-!lL#?Gv3^sm6HPt zfsSnNIev6NOY}8i|CfDAxPOU+ZsC519tQgwsjcj5A_Zll&B;RXpwP!~vJ%~#(b<%> z9N#vzlS#Wk) z^kVIU?|%wY1dKc-1o%7#-=?vXRy;@JI`^rJ1JWeciEWS z3QrYks;MsU@0MiO%P%#y?LD5Bb!+>Nwa41p#-_Mk9Zk(o?c3*aw@(2})PX|K?*JpG zY6`axh7=KyB3$x1QM!(UV+Q(f#uy|#HbI?X+mE^Sq+FZt+da+qTD$ZvZA;*g_Ry7% z705wX?qIL>xex}huP_E28OXW?xU-QZ9^sKg3P%fJ?>JgU37j9L`&?O-0x!7*ogFoY zIbhi3N$`XgD>ogic(9^*%G4iKR}?-}k&$`+!t|o*`u68L>I1nAb-J_WYL<(-hkcwl zwctkYD%^3)xCN52fn-f!35kElMF{o<(ZC|?Wr@#h^eqsmI*Ev2c7bQ#P;$qj^-Z&8 zENN`jw$*G8{6Nc{w>&+H|cEnm1#*{TJ>?1}XKH)kJ zrbRe3yaJ1Y*W?Lwzfz$WbLQCuKN|zPNhd-#O!L4Q;K( z#TcY~dHSz4t-gNySXSOs=*9O=7T*^5o%UEWIs?~KU;;fg&dDXH#W%K^;gu_nBEyzReObnK>co*(Ec7w6 zgrwDqlsQa8?Ra>J35IYt)Q(=K&$Xzbj`gFdPTQWI-qsQLvF4da*=WybqqLm}MPb?r z9%Oh0i0$X(RiHRrv;CBIBMF`ZB`}JIXt6H>RH1bMqw|nKkQTA2rD?diVQbWEcYf!b znc8FfFHN5|ZThAC?Mn|87u|Gl5o9Pu`!xa?=wu>)%X2|raOmRQ=pcXW&izsz97MJg>5!oxGB_Z^)Cn2r zhp<+K<&e?;^-u%FQsLB+v53b(zcCMK!5Vm|E3<`~%{Z;Q`R=xkT|c9|MbYmgAh>>!b${{rYhU3WcM{eva?TF*x7J1U2jDplm!ifhPY?odlD*;M_ zqZBrbVyjxrO~s`!PurC8Hf>#nm&RD1Nx#JKjal|t>a}N7Hq48AI?|!7#TZaoFrdPU zv$3Jbf&rBU11bv!R2B@VEErH(FrczvKxM&z%7OuvB|Mph9Ddaa%TIG$uya zA<2*siyr{`E*$sZco@giI9|c=GaMh__#=+cVn+h1dIEZS%w-vlTLK)n1jBI)_hH6b zJO|Up44yI5BfYlxZ`c@OtWI(0=BfH_PO-<;d&jgX-@RU+<+*J}^X!K9%&e^JJs@;26GWSr8n1Me0VR#s}!qBrW2 z)(v@X9Ouy+wLn^9oQJWPjAOd*T)AyV->~*rU@?w^+E%2%ySe&WJa3HiCdPN+F(3PO zgU|n;ao*Z3Tc)n;8=KvA+p^`TrO3x0xQO#zSSrE+PI-`b8S*p6dB*sTy{=JPK69LB zOW`=LzL4X*Wuxn7cg~+XvT^33rP^b!zj68U^>@DdRq)LX_K9zm%$6xUCrhu2?X(4D z!D%SY#~3~wZ2{%NNvt?v6}HY;snFtCwPLH1VGSH_vsA3MP5U!b_Rel=nR%pfYV!lr zO=&5GOO|O7fe%-pm6|To`!$=Rea5e{ii_YwKtV`~c6E z2bZf}{W4JhtiCt}%|&l*?V~0*P2y9letkjMXJXbgE_R&Z@fc?l4DF#Uy)}+9IWx;$ zIK9nKvPJu9AGhLwp9>ZGH~K-?c80o}6H~@ydyG^Lqhk*Dc=VmZO$BQf`<<{y!+QzW z4Ufi-a1v*kIc3N;I75UC_en;($STQBjN3&hFsIC-aZch+B{b1=Z|h~RQfRKq`Mf{%wrcIVcY7jsaUef#{F)1#Qq?!Y4G*gx zri;X_pz(1ghMrg$H+uWwmSe*6=cp|F6a@|Y0<^2J#*9z8ZrB=jAsO?1G0XzmQy%un zoU{!W6^CKTc*uhGfHNVHA{3`piG$%1%Dkm)*g~<@a>m1l=}ST5`>iu)&73tlebLR+ zcDA**9Xxojtg+0iS-$nHz{lkXLeU)Uy85opi4Pm_56=cn7z!N*?h2zX&9&KAeKnhX zKJ9vSbv5NxCBdM+OfQ8s7pe{|k1m<9;#G-PM5vBM78<3)NJEE_h7KbQ9Yz{Dj5Kr@ zY3MN0&|##(zS7WPq=h>S`i5z!F4$mbbQo#qFw)qQai6RQ-I^!dt$EO`dC;wS(5-pU zt$EO`dC;wS(5-pUt$EO`dBWYA2TVNZ*0B7^PH_NRF*f743&%Y;9>(!Bj#qH}495pJ z{)i*gja5RbN=Q{1mZ}m`VSNleBs==)9Fk?D2o2}Xm=pHlwHaO)VZz!NnsQ5Rv>x70 zn-bdY=;P+b8n14vPO%jj)0FUh>Qk_oZ0x($IvV=sm6k2&Z)ohFTT;GYpuu;`w4#EJ z6+Yjp=|zRpR!-^6t4Ysyl^1&wa-ymW=jBxAB_`(A=%r=zRyQ;ZEGR3R-;eV=ObbuH z#aDAnM?vAVRX*R!X@v`><>pPPs&0*o&TY-8Xv@!^>P<)anz7yXxb_FVR?4>mZ~pw6 zC2DN?`(nK4Os^mdTD+|qmmW|3_PQ)mrPZf$Oy!To5Lr3deA>hVm8~14Ws=1V# z;?ZJDrhC&;y&WZmGwWQL)w9a07B{-w%}c5(mp13-wk(@nkm`tzNiAJGGa;dU#gRwjgQ*y2B3HJ;6` zD@sW%uFrPW<6JT&JEbT)J}$d3CAla&E-tf3znoHBpW~`8PDv@6;$ouWxa^{ol)|jI z@ENUWs`i$;sMn$O;`}Q84DLNmz^vfKyY;$)nnI*02+mb|(4fbtLR!2rL%`n4e%)}{ zSl#1{(F2`1w8B2jHW_OQv<%E3CTA38q?RV6M0hNoyyAkw{G<#_w(2lGrESoSy(n14 zb3p(%4i`9v`xrOE^hi1P#y(VL1#k|p%CHOIeiRNJZrG24xmdAdn~045+?WB9Arv-4 zj<0^y`!Ml{UgFfJ_oL`Lupi~qyHWJc1$e6rUM2Q#T+<8KYofoHf;ZnD8jpWtJbo44 zhntB1#(4aByzw>>|JZo^19*dQBL32N{BpcGI1&HSc>Mi%i|`@f7z25JfxbHiHKChR zGh8S~ZwpMqz6e*j$Gv)%v*JjeoI9~*1<8dy&l-%yZ5 zxB2(S6WTUwH$SrUrc3WlYu|6WwOe1%`f~d#?RsS36mTgC&gEVV$jwzKtGaa^PKY=5 zG!RF32**Lu(}*XIM*J!@Fe#om8u91RG!hphJ@GQ)m*b5;#z%qcIVmTOd{9ooKS?}C z1?P0~H{w?X|HycJ(@CjEb@Dgj&*R-b#`8VGztPFxh+iIjSNJgBF`eayEgIne0N(Uu zzTB{JQ1ZPj@l)_l=R`d7HR4y{jn9dA=4-^C$NQlZ@#JsBKY%wqC*moO5x*R7VLpUf z%=r)Qn~p?>;!<-a_5*PSgf%H#XXDWJC(vV+wx6yIN6Ml4L-&s(Fd7m!^l?ta*e=Lz z7u*_&eNZV01)c=i6JBUa_GyKydM;L!-;!B7=9HF?gKVFeq#@5*>&#GX6IKX^Abn_O#W9J z`S1o~V{30IyV~$tIlh)Ba67np3iX!rGNp7iL!E#v+e9~M(i$x~T4N)!F*{P@dq1zn z9UGD0$w+f2*`r;)yqgxeit;_q$egnLlBory%}4b`silp%iN*QZHa(}jHZ{ApAi2HF zTRWxQTUk?6S{9hCeft=2lr=297&tbdJgjQU2Asy@>&D|(h3c@8K4m=q{L;zkqsHSO zm^C?GACF(YVsgA|JpTUAAs#l3brqfK0qJnB%G3wkJIskGdRzH&PLTK0uyYou8D+|W zQ~o5sh9iw6<88HK_+`cL%ZlNb6~iwphF?|;zpNO3Suy;wV)$jnVIQ{`)u?89hB+{ewfmtkW(NC{))Z7stb!tW!o zu7Ik?wB@OF|*^p%I4&HD*tYCW>!u%%M^V(*V2G_6U0Zm%ooTR zk29=iZvr!v7wG!2dstc+Arv zp8SpYRez9peEUwRhjsEd;?KWs#IwD=giqgu2W z!*-e8PS`49;D{M*GT%&KyPS%aIwKUzmllotXgspeR?^^9$iFe(SDBc&hJ(ZLe@Z<( zFcVyK%~4(7?Au>kzI{cfr!dC(ZHM#Do7=R z5(BSd$h`yW-UVubS@7Cn=xM~)jmNJF;b_FCjK`nvnw*|qR?c5TectiQo>t;Q9En3KR&$l=7$4M zPj9|wX}zbo$TX{>R$tIgA87F+E%yFVpVu4s;N~i11+Cy8=vX~~#0Bsjzsz?zjYWuj zm?#UCo2iV#EmZmFqd6&Rgp77B{I&?^P#f6{5gRUdaNiW?>guFXRRMU0(fpDr3HUMHqZWS*n>JV$ZT3|R2?2_`s2FCXjtEFYaQX1ba!klz{xM`}; zyqu{!8xMX@C&6lYv6JLwqtHB5e5f z#R}=~-P)V#hk6PCjTs-)pkv^;#wT;6UB1?uf}%1#rM0LCLS-(o`FVnW|yLf>LS z--6X0tpQ&H!QE>TkLYUA_xRHugghK&tUa!M9ZnEk@GKGJ-22WgH z0r@$O zn__bp{^s?CfsdE!H+}xk?3*AF-qY0YmG?AlNX@ARX{ySI=_ebSSUc-SgUk zI>T9HtfI-mcREvBaNyj`A9xt=F+T^tJqi+Gy)F&vrc-4UP?|6?{0z@#5y!YV^*^iolX~v|aGd<(*CK z{z~d<$iw&4B2mk*jvC&BL3t+cyWpw;<&kyN#84A9F=pT%cUi3Am;qtz!I1$2ULq|k zukc^&x-Gdj6K{}a@D0*3Utn{8mv$;73Wo^kma?R*Zd!}vZ!D{WeK?t5xmH4KFc%MAYd^qRWBm}HSqAUwVerfD;oht@5KrY{28tY4MC1 zK@}&>H1|frB8)t^Pk?()SO>YD#9E>#7#KS5M+?@C@fc-iCDK9hOrw*wxUGtjJmr~Z>iQK<2FH|)5+ z1T`;5{bIsXOop|G-vgw_XhT79<4Y#uHH2BL*ek+6YFf)|H(o)JTJ-xmGJ5e z!=d*G4UE%)aaKs;NHMyJmX1u=($P2KW(Hafdy!b2oj7w%hWEHlhiU1quvO$Rhw<5L zr^0X7!=PYNlUJ_L@78*>j59@@i#it;ohh0RaBrq!$SuH>!452w`!uh0ex z@O`Cksat^RXZIO=BcE;mw=xo&vJ!7Gntl{Xa_}NX2e;w5$$b`6&&*7`js1$ezmKm# zefs_VeC`kJ3H)3ukXQJPbo%zsOs8-EZ6pV($&y?|e44)R`IUe7D;duuZ1$e#* zE)dUtd7%t?yW}H zkMID_y;3Li!IIO0y>OnJ}X%)HOxOu5dpzZ?QA zRQ48>0?!(*hok|$1xa)yW%lgHd1n93ct_|Qi8JHrCo!HD&Fl2gb#~}F59cAoQ>6lg z3m_uX6(PI|=Te;cK>)^8AxvJ)IP;pY@jM-8#?J~}&kLP9L-C74*UNEcUbo?V8+Z;Q z+>LNI!hF6DVdmK%I^T{nuorb&n}#Ih4MLp@Ck&O zKVd(GFw>ky_;e_q@Sh9C--GZy=sz{qbG+HewIVm#{UjXN|LxMe2F3-GO&PPnLH`K7Oz#}S<)1+U>x%^ zy>D_g+D)!0ft&E{Rp&x&^>k zxVsT3W)q5&4SfuE9*5u0O9=HA;SFE(99VBGI7ds1kNsm1ModB3}v;efeJY z1)U$63j6Z(*FtY&hhD|r5_%1ryyKDnBGS{FNACnXwb8#b-k!F^Yg--82k=LYqK($@eyvY9*p;BtuuWi3?N*a3|e1zhXV%bTEaUt_BUd+W8wK6w(3 zKi=EIcXs|!zO(b$eHXvD!@lbuGt=f6AKpQ~G(Ps_+_*9JUB$WX@%a?aV;S=)t3q?P zE=amf)-|wV0gdkqts{64zf@O5Y~@WzUYJ=Y$v1bD9ev z#DLIF+-f_0bYm8q_CY(LH^Q(DVR|fd2$B%yzA%Qzzh1zvb;bS3`2eeokjo=46to?GTE_ufydYi^j^ znCGo*U$#88ac)D^eJC4Zz;^;T&c|MSzP-wE8TY`XBExV$$?~Re#*Tt>!4xXiSPkV8 z3x_Mzgw8u!KN%2Vrw-}F zYb!2!6L{dg3+(9}MP1<9&ccb^TPetooflnLzIVY6jCM#F?YQO`jjoyOFJe(}hfNf; zqFnMq9^?`?6zlkyt|`^(jZLO_eRK7c)?+&k9@x))rpGVnA6__g;qG>~`=LBf;HM%} z3NYFUnWC_gJ7|nqISG}H{K9f5$ivE&fw=I9igR#bAC!fPJ>`5c+$eP`^rLKslVVh; zXfWZBEET0Jg^8W8@&+|~5_y3dumAZ<6H~rEZ_)C_m92O5bZu?=ONSQIIY03GC5!*^ z>?$pD**_oy);Y+$9WpyHQID_0gm)(g+8uf|t zVo#EkaY7i!(EEt8VfXeU2M!%>YC5!_$fGaVd7}N^A$`H~A1s?uPzaec`Fkdhh)mco z>qMi>R)uD*K_Ytg6ZHpel`+=k<}B1f8N*F{eZnVS%fh$o2_T9 zPg{Rv{SVtB+taq6M+`-rjkq2Wuou}!>{skB*?;S3b*ykay5h<_d$F? zd{6wpCln;COE{kJO2WS2P19?rZc^P$YIW;wEwvvRY% zS&dmUvu@3LI(tR->g*laXS4q;Cp)Jyrz58;XIsw6oabFu*L>Gz*Wb8ac9*$lyEnTZ zcE6l^Ywp(E!?_o7pUnH`d`JFV{OifzntvMquIB$t{=Jv~Z}}e=SPS9{vI`CtTrW&7 z94maj@IQ*sdgG^IwJ}m@)|*oC9!gXI>Ex|7$JXK;kg3ls6BS-_EDRfrw z62fzWPl3)0J_5Qx(sT-+MZp&kvpD!I&?OSzB|N)@_6YS0?G@T5v|r>|AABEAZ4kOq z=!no!p<_a~iB#K#?hv|D=q{nVh3*l$SG3qCbidF8LJx};M?{OGLXQbOF7$-ZlR{64 z{HKMU5qehWInnNtpnX}SdJ!>MDg&IefbBb=9--dg69`vGxDwB1sjA@DKpTVq3EBh> zS-|{vpe^8&rDh486Z{C_`N8)<7YOYP{sY1bgRg=vmNfmsbA6DX^VlGCqtFqdqe91o zZjzLng>DhLRp_0P`(EL;Pw0N32ZSCGdQ|8!p~r=u5PDMRDUt28&@)2M3Oy%xFA64K z6a+4bB$tK0h`ZS;0vchXAvDTXPS|0#%7Rqc$}6-o_#who;Fhg?!8bs=B)(f{k5IqR zUZH(LzaS}J0L=ma-+(p%+Z-VFHt049Zx^~l=uV-#gzgr)N9Z|8|01Xhd|m~0Ko_hX zpq9BHC*v!jg$ok?0<;>|>w=s=0_~E!-9meW`i1ri?GyS1N%;b(8~i^6%|YF9!yeuS zZI+l8=2i{qij9j$7)ETe0rA73+>$vF^AP>yBI9 zDg5^epM6623q2t8h|r@#j|n|4^n}opLTT-}kokU4hfrtmc7&5rnz<@PXck(ATxj$w zP>-ZfJ(1St`2|Xfrj|x2|^tjLy zLQe`kCG@n=GeXY_Jty?CNcbYc1;FzSPzPF%0@&DhK(jE@T>x7m?H1Z2)GxGGXdh@H z+MJ(aV{S(90Z?LIB$(s-u)(h&Tmk%xV5={HR)K#J&}Pg+*mDsu{}7aT7AfLcBzP7H zo<*=9#t_dU*bnI@d3LkVEkd^nJtg$C&@)2M3Oy(MF9y%y*)O6eDpHqVy+w+;7pWI< zw-mZR4(br<41ONrmf){JX9?Xabf3`uLJtT%BJ`-xV?vJ$Jt6cYXc;j1S9py+*ljuL z%MU>p!amE@7bNBdP%m;n32G010n`yZ1nLal2bv7@ys&4|9Ml>wP$exuzu-ly@-k?X zP+GND?AZ%@W_X@ZTD2EB{uq?Kp;vlCFVH3*w%cB5x4l4{VYb^|piRnl+l%&!9B92> zMeFq{TCZ2pdcBI)>s7Q~FX}ZZZP=@5!(K%j_A1)2SJ76zini)iv{kR7t$G!0)vIW$ zUPW8=D%z?Sf2)s_w(7;-N~GkMfTtIwMyc4-RRF8+fU@1LkoK@b+Pwf4B6yH}yu z?o}wZdlic9UM0}x?sPkPXY5Bu-LC|PuYe|_U9AM#q-;GZf%&gNJwnS-j+Mat7oaUD z!AhV_I#+luK+9hVgx?2c>rn|ANZEQ+LIzT{9+i-Rv|o6xM-N*G{7E+o9T7SzbWG?r z$#J{T9YS{s-6eFl&^Rlx93P$E_(h*beG%t=b!uL5GfLbw4vLKXUv*FlL`75ukffD*SV;KuL* zpjQRln7&ImbPMef>KEE8v`;8oi7MFbzk+TMx>4we&{3gdLbplo+lB5Bx>M*bp}U3d z5lY;uV6mjctqK-PO5Cbov82SU3KmOB+^S%)q^Csw(?ZV(JuCE_$a#OgO;Hhh`kNkBH;x>7o%ORhP5!wdf0h2v?kpsbVTT=&@rLg zgu`~BJB02Ox=ZM8p?iex75@8#?iYGM=qaJ6g`N?5R_HmAo8{Y)I2v4Q~dSN*Nw*3yM1M!V0 zso#KRz&`j0>tq8uBM{->byDw`!`Gt&Gk*7q||FIu4cuR*s7-7a*8(49he3EeGp zkI;*#BQ4wTg6S`mM0ij2P9u;~_=y9PZgq{@oAj+Z@68;+W5sCRaMtrU6S>V$K3BLx~ z8T`MC`U%~;r%jZ9x$1Y z{*Eymxy(oFLCTTKe6${<9J$O#>p{wq%Y3vRq#U^{K)z3bHUPr~C`Z!mLU#z=DRh_6 z-9q;Wy@-4lK+a#_Jpi`D_?il^!dxT%ehj{n3smt=1TgFb=RbfpNeun@PH6Ni(5VtX zOTybEyj|!Hp*w}{61rRH9-(&%pS|+bKB4=C9uRs&=ux4^gdP`qLg-1*g^=@MP$$Z3 zAtd}EC`U#MWyG-%nEwXh7UZ%J5;A_4ggIhb2xLe(Vp<3fgp?zuh44T~IbvEUBa4M7 z5pvikbidF8LXQYND)gAp<3djeJt_2y@T(U=!jC{xfbb&7c@?w)cNamead<$%H z5hNs?BX_q6-7a*8(49he3EeGpkI*}X&t7?UpV0k64+uRX^r+BdLXQhQA@rosi>PIb zU;}T1J`0{pq+TqMda(rk^4o~XKx?)HGXDa!8Tc;&wr_&oDKUE`yie$Up$CML^Ah3Q zCAxQ^hk6HpPlPiGUGV3h0Od?V7yS8WK{=DqB{K@Kb0(on>Q0x`moBL<_=_Z{ zab4&INI8?xg*y8xD7~gG^l{`#kF-lX(k}5xyTl{y5|6Y?Jkl=s|4hl5gf7LIgf7LI zgf8gC+*u#Hq&{{@ee9C@*d_I`OX_2n)W&oJr_{|4GW3gf94> zstY|0Qf>pO*)h$?c3s&8NRkvW(Em(C6R^5VCw_w#R zSal0l-GWuOVAU;HbqiMAf>pO*)h$?c3s&8NRkvW(Em(C6R^5VCw_w#RSal0l-GWuO zVAU;HbqiMAf>pO*)h$?c3s&8NRkvW(Em(C6R^5VCw_w#RSal0l-GWuOVAU;HbqiMA zf))PiJFw~zta=2i9>JJhAZ1gjpwszJJhAZ1gjpwszJJhAZ1gjpwsz?1uMT`}U$F8E zR(`?CFIf2nE5Bgn7p(k(m0z&(3s!!?$}d>?1uMT`J_Yd1*=}cs#mb;6|8y%t6ssXSFq|8 zta=5jUcstYu<8}8dIhUq!KzoV>J_Yd1*=}cs#mb;6|8y%t6ssXSFq|8ta=5jUcstY zu<8}8dIhUq!KzoV>J_Yd1*=}cs#mb;6|8y%t6ssXSFq|8ta=5jUcstYu<8}8`UI;! z!KzQN>JzN`1gk#5s!y=$6Ri3Kt3JW1Pq69}toj72KEbL_u<8@6`UI;!!3uw^5bblH zVAUsB^$Avef>obj)hAf>308fARi9whCs_3fR(*n1pJ3G|SoH~3eS%e=VAUsB^$Ave zf>obj)hAf>308fARi9whCs_3fR(*n1pJ3G|SoH~3{peeNgLftAtMo&ow?XNX^ovi@ zFFr}X_$2+}lk|&E(l0(qKln2}XK@F>=R;7=vJL=G#?UJp0G{uGa+Y;KW?2V-En_&# zIsiOh1?4R30FdOfoMjyVlBAqv9RQM~oMjyVlBAqv9ROnF!&%k=AV$hr)&U?!%30O{ zAV$hr)&U?!%30O{An-o^whUz+6qyG_=0TAee{%+D21VvUk$F&L9u%1eMdm?~c~E2? z6qyG_=0TBpP-GqynFmGYL6Lb-WF8cm2Sw&Vk$F&L9u%1eMdm?~c~E2?6qyG_<{^=J zNMs%onTJH?A(44VWF8WkheYNfk$FgD9uk>{MCKupc}Qd)5}AiY<{^=JNMs%onTJH? zA(44VWF8WkheYNfk$FgD9uk>{MCKupc}Qd)7MX`d=3$X}SY#d+nTJK@VUc-QWF8io zhehULk$G5T9u}F0Mdo3Vd01o~7MX`d=3$X}SY#d+nTJK@VUc-QWF8iohehULk$G5T z9u}F0MdtPJHebWv>dbfxj~WVM&Y?pcy1J) z8-?da;ki+GZWNvyh37`$xlwp-6rLM}=SJbVQFx9B&k^A{B0NWg=ZNqe5uPK$b3}NK z2+tAWIU+nqgy)Fx91)(Q!gEx3jtb9F;W;WiM}_C8@EjGMqr!7kc#aCsQQZ`w#ZXk2ol)F3S?oPS8 zQ||7RySwD>F1fo)?(UMiyX5X}xw~8L?v}f| z!r_2$I3OGj2!{j0;ec>BARG<|hr{6TF#Z-YXPgcr{->avaXNxl_cc&j@)4xD1j;l= zkcRP`aXKn#j!K%NlIEzSIVx$6N}6Mm=9r{8CTWgInq!jYn4~!_X^u;pvvdj^eu2LgO%A7p!)f7gS~#2*4yT2~Y2k2MIGh#^r-j35 z;c!McoDmLZgu@x(a7H+s5e{dB!x`alMmU@i4rheJS>bS2IGhy@XNALA;c!+soD~jd zg~M6la8@{+6%H5hEWW*r{`3jZ7K}PCpeJR_EX-Y8Kp*-Kp!2XUc>(+xzg_4Kp*w}{ z61rRH9-(&%x4pt`pV0k64+uRXoR11UCiJ+_6GBgdUPRA+33LYXy@=eu4B82+dl1sT z2HJ!fl?Ng1v!JbLg&u^=jGu-6^g+n{L(qA!%m*Pe3@P7?D9dkZcfDB`}_HhMhkaF$g3M_<_Yadr&A*5XUxB}0I={aY61)dKn=WMU2 zyM*W6LJtZ(B$RWxSKtwmaxV7@JR(xg*iREgq>iW4c2uH zBP_;n-TWFxB7BxBV%OmH{SK7-5U#=NBjvjJHF$lbTsOallH#*mH@}9l6)D%vuVG|F z%60Q=7#Wdr-TWFxF67U3^J}P02SB-Qeht1KxpCcG;Tu2&+!My1hIeX1VeF5_l@X7> z$ETHt!X~_W74RB8I3v1?W{+p$@*&V+AKNf_Tmw9E~K@%NJ~>q23Znr+z;3Y(GUWp^~lMP#Ax9%sM?3cIexze20knI#pf7PeS2HRc&`X>Z)pJXmBm@-{G3? zAMy7N_j_H-hK5I71MAm~xp*Gw@89AdLG+S-|7w?iY|P&`)W6y_*1vVkH88xUA1Oy% ztNX|N!)r#pt~u)l*1HN^n>MWWkM)nbHV=;ukGZyN+2UPea3|YQZ{Pa0Uf1;X>-=NG z>(`CC21k&1VAGm4cewn0ef=B8z~BGbk5#M=J>?x5Tf4^VYF{(DK9tMaJ6zzqwtu8= z*uTa#cE<*awrSn!5pXq_A#VNZ{*krAeTD=Rif-|bhGbst8eZqBuB@!_&Rjn-xPEM` zf1PXQrm;;U!=qzM`Uf{b4}cZIs?qh=i5mHI?O(^=+&?l3ZCy29+`lo+IRA-Km{cI4 zypR>3W0ClKf%@2b*ZP5h{t?%vb;BDsK~-upu>Kfg& zVZ-{7F+60zwSEK%*Gn1{5sG(#|MuZ^gCQ2k1#q%R%EyM+_Pai}zSrenx7yXexgT(b z%b<){`PTv8wE%jo%+-x7Cxx#t| zTz9PB7Ty3C@1} zDRUR-82x8kq#Ejv*Rh}OJhc&n|c#iykjhzdSj%5_b|BFgv%t$FCv=X6hZ3$tF zOKff_r42DCi?vsKLoSirl8F?iWHcs;Tw~prTz9m#T}vdnu1ldtG6|ES&e~2J_WLZiQ^nAGu*W$*|mYyG{*+G zM=)0wY{b|=(W}6 z8mr+>UBQe*BJGHBEosJc0%ruWBD3-I`x5Qy_S+jxy{u6=(vx!oxpok($se^O%I?K+ zzpVww!9OpSqjr5X*W+2_H-8KYn^BuX8;gSDY<-TP7UCL-JqaONAej5av6P7~iPqS- z*wIj~O5``5vlDn_GSJ73q|JvfS$E4-LwD8xmt^Ppb=NIY&0(&`HkLN>fv&~=_4h01 z-|a6<7Yf&qyZdEh;p>QvrLQHXOTMfpax9SF-aKq(Y_wxIt3S{Ftor(NN7Jm7M5HF3 ztCASgzPJCjv+?S2wUuMX?92q7<9OJ(nmkxd1*Oa0_e->PSh-e?FL}Q9zJ?S;(xMnj zzq=KFUwrAz*OKQcBLexdQuXnJX4kQYh zs9={8U7hIF8tl(ei)hav;xdEjPY9n)b;$Ftr~2%$(|}$!BF?9=ny8tY6VcLwNS06{ zQd%)rZHO6ZM?6M*Vh-LQbM8%cTkJ?Ob(q^zrnAc!?n^L)@-B-720n*ET3g(#q=ar^y`c>)Lt_&n0TbatzLUdvhJHhPKE;MHx5^s`V);xw4 zU@Tf3=k&{LVKVbE5U2kCw1)fMJ&qw<#o~S;zyyG5nKLqGeKitUrClnX6*VQ2@Du#d(W8HSC9) zua@*U_8{L2{4n3Hb&L5d2}=>lbCaXS(yS|QGRLLZZISJ+U>R0hH<<6zuq>;s>&&~6 zuXcsyRK~l83_Jp#a4b(A%2njyQCPu|P1kr`QRUd*u@bQpSCA3oQ$&DVMpnwhDrCf7 zMrMpv`G$l`$j+0nnqzfU^e!Stm0%53hBX~)v8KC#Tm`})zMJL(@>Rt}Qle5TTO;%nnJ2qff%rnU4bFdN2Lq3CG zWAbS7n73e7YE2xQvWh&7+=jsBtRi!fUthroC`(IjwlKGS@vdCg}8hR&?`9`pVqs5t=K>jUux+A`ee!7HeGU@V5_TukCkI&zWrb^Zv5%l7t)Sg=Jc6#YW*r?)9I4%_ zY76_oLuf}k7{N;U5L(h6M#6(=%Ih$idnE_anGUSJV;$p&h(3V+yanUoeynOon5eg5 zlEUy9Bs&h!JG>sKPToFrt26w-aj?4ZdWhb|Lo(EHn7Z=%Lv>>Z(BXJF_M&^nkMMQu zMfZE)Ir$i0$L|{DI2w<~9(3@1I99#jIJg@-5e~kFCBX{0xou3f@dZjTQicl;-z?Ie!(&sQ}EAh!%~fgD;!s93~_p^ zG#0MbIJm}ftv>P6uwdiiIy^RMS`W9f$HxS?0l&>wZFJm(_ht*$Z6e%)4`d5=coKIg z((r_A#wt&S+wg>J#wJ@7dOH4)P1xiqFavJHMo)#Ang+A*Q*F==N5+-cJM}f(rRmWxf8;Ot=pp%Q|ebkxvGAK(oA59mMOBinTT##`ltnZJmur zF30gG(U@zoy>q-ZI<6n!3CF)Q7hls!&4Z`(Bg}O?t@-$o^0WY+aXhPq-b$U*B6wbl x;RVNwTEbnAOZo|3)>3!{F4t8pgV&S-uWLEH0aJ8SE8s1ygnz?j^0dlZ_7CgM4wV1^ literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/GeosansLight.ttf b/vendor/szymach/c-pchart/resources/fonts/GeosansLight.ttf new file mode 100644 index 0000000000000000000000000000000000000000..055932aac8de6861ab4e3a8f54e54d1f170101de GIT binary patch literal 60072 zcmb?^34B|{wf>zeOY$mPmL*x1WNWc4$+9GCx8*Id9XoOMeRKAe5caT^vac;$DYUe- zeLzx3DezJ#YoKI-kOTrD36KO@pe;+GkM~|nTS{x)|96fgFG&pT`~8#X^LXyunKNh3 zIdf*_j3Nn1l8U4&B$d=OeRe2PcJAxT$!w(+c0E9$>L?njcO9fvYU&scHM*{XU~HPWbGa-6y1j5TkZ z`{Myg`sf!P~D$-9jg|%XY~jj!U(&LGsIsq&jJf)Q|>oKgwi?~)#qf}&I^m(G#OMU7;Z8>MpDDLsNuJ3jsR1o5%rW5=fwpDui=rOi?l z?f66++VDzs_!$&$pzIvUC0{Jn$RTNp{3|IUZ!(JEET z#Zs48jr{A-?#t3t?#CxwE+u}C_x>b##nVzi_M1n)kFZc&qrJYi~bgh)Y&r+1B zmVP32%Co?a0N%Y%`KR<3$&XJMe4eOkm5kgUi&T|&DWqnz)I#MU7wufWUTPAXqz3sk zDJC9}n#D}XL+zt~0o8e+8-3<+eL$+mn0mllA*^jhC`5b)h#m^7%`4TeV$c-Khjgmag6aK=t4fyf2DE7_>SYBlpgunAhq!C zG{z%7OcTihjdh3ABCnR>S)LGYzRSEJUV^7`8|3$oQYCmPQ5qU3XDvnrbQ{)0RNYaC9OySA?09XZ3 zR<2sTX6?H5XyLRCr*Ayt%(Kou=iKwC!A<91aN$K4U-FGhzj@i^FuBr|S6zM0wbxyL z!;Rni_D#~wx7>Q$cW(dg9d~~3`*;1|hj;(z$3OXxd+xn&^Om39|G?ItJ^1s79)9FM zxBcSL$A0#y{V%_A;MLa-zJBQNkvHCa>*%rL zZ@=^F-$;4VpU`Shav-I{=#)rS=`!fTLy{t9ixuJ`al5=ne#cSaa5?H7O^%e~A*ah_ zd9O$zyQaoWWWl@lNmY1l!@^|GkT(H%^k?J^JpV-+a^YF2*QGC5HZZrG}xjB)K@9vGkd2fe|LVeV%kVEax|+ zP15<&X~?-ix==bDKhKcPlrECaMc?R;Notg4NtZ}BNq326v07}w-yPzJtdZx)zf_r2 zwW?{VGgZ&34yt~m`b3q^E6(f4o0fN3-mbiN)D`Mk>ecEOG?J!PGfp!@vr==eW|uao zov6J=`;7KI?HBpF{Eqy2`784;%D*FjOa8O@2Mgo^Z^80{TMCX8d{|gmXe*2r&L~`4 zcuC<+g+DBOsBlN&tA+0r6&JM?tt`5|Xm8Oc#SO(1i&qz4QG8eNmf|;yKQE~*nNxC2 z$^9iCmi$v^(*h((TcGQR*m7ls-}VPFZo8t884^tg?6XjrtD#75Y2$ zTlIVOAD8EqmzO)rr<89jf1-Sk!DX0ZINR_@MOnrAikm9#srb@ZZM?*|&G?EbXgb?; znQ4n@hv{Q;gSpxKrup;A!perqROQ;rt17ov{>GB9++cao@`~k8RgtOFI=QP(WjU9J~g|KoPJSGuooZ*_lITU|S^ z_O{yJ*EQ8$QFpZ7Rll_UhxNxjjh-&g0?*l=yF9<~y1a|Lw|iglIeZ&^-}4=9kQ%%V za~f{Ot0h<6B z^ZHBDALTP3>#!t-SiRe&F;<{>!c&1(>f^CQG-CQ=#OaLC$5>xkSx+CiDw7d$Wqz-x zc6*f#nC&I1$sE@bk$AsTxHYB1SOFS3MPiq^v{0|rXbeGvQ0vRAhVt^uyrr7Ll9Dog zeXYi1LXNCqV$~c188PYvSLZ zxaih_#RH3OyKP{BN^N28+FU}2ykbL!!-lf-wv)KtUs6y|LLYHjF16;~wYxKQ1H(6o zB`e#mxyv)K_|{to1_l<~ptevYi!>#g;$j00oT1Ei!pQkDRhATIe}bW~H#Ok-Gx8^r zQL-TwcW1b!u?x2`s?YrOATt^k zBqYXMkHN-RM-!KLTU7Z)y4u>>JLJUR{!RM)B29HI%c2bbG%39)zlHa-&=a>g3Ym$% z=_{&O>nk+MZ{2gxJ(>PFq+iR0@bRq7L3h%v<&QS1b>kCF@+av|M(KfEcy)DU&9~{3 zf3xk1{Hu_<$!T$1mw$up${*NnvdVdv<4@&zmdh@?`fAI0=g~B(l4_OR;(0jqPRtu& zNvex^^d@t%-W_)vvvO_LlYxMI8>8_^GND2riJeJ91w>hI63;&}$ypy-;hhi&1Y9#N zarw$)@qX{zP{W(as#-^N^5Gg+&$YY1-6ITzZTcngcvEOXzu#p`KXkcnlFt`Uv?WB+ zWN}7gZfAbFdV)VJdJ@!swd7a65Pt-Jb2VKTBksF8>I0SQpS zu<1mp2npscSq=l4i)>dqL{g5L*8gbIf;kh~{0*hm-gBl0f)m$Y(ixf_6M4Eqi*Aa? zR^)K_V~!f3w-rsDm~gqrmBf4p>~sGs($?EE(d%h*)I{56Pny%~Z(OHtWqr<4cQceD!K)IMQU(7%Iz) zwbhPFO=X}fzp`G4`o>s@Iw!DZy+yQ%t1&-Hb+AcpBkUiPO0=yFu3a1a+_!cu{-In@ z`GIt|GyxJMB{5Uo?d~s0ly0U;RzMRzmp%}7euf%8IQ+TK$N9>a(j2Lc=4ohecAlP- zo2RYWXZy)WFVs3`Ls^2x^rJ!H?ymA}f>^7~RL+ndo-8|LsKVdQyDu5O)p z_0ZJ@{&oY@tLG^Fnn+tLpnu+>S^gKHc}R!(TK9?e{DH z5Xa@?<1S4GqD@ zR4CLH^*5F@>Z9@gg{joe?=@6kJ3AZ+G)}wB>**Npa<{sJGslMlPG`8^=Sz}tlc{~V z3O+@CRvPre2~qGkgTmGMwS&LB;RdhxDQLH1d}Q#>&9v8-%E@Tl zsY*l>tzbxoc&3=#xnx3?XG~k(;PwA0{aIg~tF)lhF}t(7t!>G)u*Y8C`QSNyy~?>g zT^g18DPQ_iU7k=ST9zyuH@?>1TKc`o{Y0Z(`ENNN?Nv$MOpESP+2B?|7j?3+!YHRL)5hMOn>H-Cpf$B{?nUh#5CN$YeKls}1e|dSvusQkiU-rvT_h`KL7`Mq zm-1)vTlr1wUj(Ib845iRl_3j@nI7&|);CLFV$i`s;OPsbj+_}f1b#Q0#Bal{MBHAz zcGk>j)LtFwbh}$y7cXi}jrUbVVLK~)v)VgmS;C=76PrR2O9~@viTrAMtgb>I2sY1J z8;j12hr*?m;mG(!EzK=0;^_`wMP*fmudQR&tn|vhV6Z9F(;ca_+bvDR-)i)+Kzrs?Amtr`J?CNO7W7Wou;HfO-VNaJE%lEUmZ~*s?*;Q0rdpZ7;@F1X z-kUaT=$!x#rR`NbZ-2^0Okf>+rm%{8$OhEKJ;}IMSRZ}# zA7=(OZVdc@=&O^S6k4$eBMkot{-V*0kI?@1x4#u(pYIZ{7xEEA9llH*!ecaRjl%bj zU;IL7e?YaMj-WCqwn4xAJQHLriFw$L&{PXvn>yu0(t}AB;@c_6uBey^m` z)EOHeh^b^vQIXSVtBlpQS2?uNw37;F#=xcQl4D|z)PY?^sV-}WhGZ2qjkdOAm`Jy= z0wVu*NUGU3dDw2bUF_vXWLxEosT?`;m8-jvs&3l+>*^w)(t_67eZdJvB9&d~IqxUfQ^HR?t6~NVSNE zQWHg9tt-8$KNu8~8)?r(U|siD`A3iu7s=re!`Y0PF64 zo)T&BHTDl=7E8LSqUFs~Lk$K8EtQ_CF}kA;y%j4oJQkt=(=+6e`iN;xD#R*?bv@fR znpU%&lP{jT=(f*3`_7^{zWFoH+_UHMX_Lg-@2=T6nAo`Xj_)p*C11RJA=BN58h(Ph z;74FO)aO3pC+R~X$e*A2#KcCC=NrUiBdBe070P2oM7BKU-K)}n61wz$FIH4s5}UXg zo~ZQT37u77MjFKxI2J=3!KmodAN{egci{UsFCBR1=7*O%&)jj(`gQ2`gX@-wg}&ue z)(i~Xa`Q9SSiZY{-92E%8LO#&;zNCwcC47-17HQ{)XVk0^o3rpxKZApJ}d%*30mt> z`Kjpf`6v&6h1TTfd*%J;lz{F0LQ*pI(8{X<%0bhf;+q_{xZRV_yG3pr+;djYG#+KK zT;zF^_*Ed>g4;}D7aGgSd8zbHpHCwei1WqV^ls6Pre|x?4`gW-?P!C1mAD&kiS~V7 zpL{QIm7bfG&s68g@3Ev8siIbu>YQi({O4zUKlt?1yX5xMXYnGI&Vj{5JIkYZmicD_X)#tcR<;oWDV{xh)+65Q zW?_Nc*w}ULwcWm;Y?-+n99k1;nl-+@Rv27$7@3rEkIDj>A-@skEi;o1nW&RIM-{BO z%5n;_c@Japd52F)Ikq4c5B?m^ZieS{6q>@K21} zYhn|jCk8q^A{yM zAC)TyKkoL{)p{mSYJM~njG>-7<#X}Bs0Xqx+(p7|5dZ6cc6-AIP`5|rX@l5Q7Lp5c zIUD;HWJlaZDz{Zu&2HHK%g48SKX~j>6nsjaJNR>X;$WjZfAD80k10WXfjUX=R0ggU zS|Y?3hxUYiu`RUcQ1IDbi8IAO`ehMMKajpvOin*e?a+JgyE%dSv z_JK;@oxYn7&2W|`VUHxkBk~hr4CTUfTwDn!`u+5Jd4I#;av$n!RSroH%6}$a!kd7r z?$$rp+r2U9lHYOv^Ig)3%-V?NA=nzH3LGeU#e0Cw4W6~*{fDye>uKN0ZG32BPhU{w zbPv{I0-^VP;$qPz-;TLY5-KdNl4j9%vGd}pv(Ll>OZuZ%U(J-0ltyv3GK1Pt$DKxT zcKWs>wQvEcj>R~)`kQ<%Ju|?M`rCD;tHmF$tGt$U){U{=BQ|56gms4HG!BU)W=Ug` z$3DAzU=n+LU8@##w0E>GT0K5L8kyL)mO=b@;xy#p$+W&#Cq^S{`v|CQSxTb*-{5SF z>&NPds)ry&<1nnrtQrs1{~Oe@e!S1uwGMT8hZ}emzs?qS4BDp5Mu`zE2q^^Jx>{_J za0ZU~mYptI*wx5bp=Y;9#np+4Q&xtXrn>5FRiWtvms~Q?YmYXqoH8}sR8p!B*IqI( z0H;C5-q7F0>p5C!7Wpb(yzZNj<;B`A^)RXO@6%__%rIkS|0rh2ko7Ob0nkdPHndV4 zSv|RoSv{+BvK1Bw+S>+}w70dlE%DUM^whchu7+8m0A`e5c&}&=h2L7-(t>9L?d^qC z{rY6NE)t&Oxy%J)Nn=jh{-yj8>97H;KvP58m$Y>mHGf%JtT8Qz8D6=v*(yKo)5^;} z~SjH&lkNJzF@22bb2>iuex3Lr*dt%LP1L+=f) zk!KG6C-J)*K(vxw@?ck$WgN=-oj-3+J?8V7kN)|YN5vloZ9B|wfwG(r@_W+BtPk@0 zK^yYPuNV(~guQa=Ga^D_LgppYb4(GG#NyJZm&e!Fcg(DeEXO4$84H?=gRe<$oqo9olRqqfn8xKiAQeINKBymiRdE63O?XH8vf^Z9D3 zQy2VW`hsL6z3LyWF4xt6%gV&xS%!7kfrJc?65)&+kre_geGgo^a8Y|lMj0~N9EQ42u-%uSWeZ`G=FPogx@$~27w&4acytRV2 zSOp6@JJ4i@u0y8n0AaOwYb*^VK3|EcT3=CHCax;i)tj3!P-SKHHAY=AjgK96A_f}a z<%0t`e*xaYNxs5}tr-#X7d5&(<)(>oS9Q~bNR7i`idgFkolE_eO1-;jd9&AZPO~%L zh(q;qTc@k4siwLiTvc9PSgNUV&oJm~jPqTPI<$}Vmh_%Hi)7i5+f#(hy|>&4a{<@> z@yFRvaM^ayO6OaUCH57FqBw+~PMF}poZ~&O)mr77F{$6@y<5oX^fw%iR{A-0UL%YO z?LU1c?!)`TKEi#$?|-jxH}vsIF)593D&ogy@}1(r5k7)^XYgCsT>I_Dr?0*e1$T-m z>Bq#l^i0v8egfsO&np(A&YUjdC2p4p7tSWA$$ke4>Q=k&<(F4 z=W~>z{iieS8{lf9-FmkXz9!nk?iDqz!SBfoK~JN_R9BOs!esCnD#AT>EY8I$bFtO- zr}7@+2CktoOTw3r zVsnDFXg9^3q~p*@xn~=PkHO0aKZC2?ui|r0ORq^^nmP6?4P=iuwI>{ZHfHk&8?y)KGWICn zlRjZyVds4$w>%>__eqv>=bgww-7qU9mGV#JOEJKE{06e1EanUTVXFCEP7uw!@(M1)jSBw9-=pvlP zql{KEDqpCoAvZL4WzPPlNlQ+e#ZEX5tULKQur9YE;u+J#`b1_g^XmzxgR1HgXMySB z5x)q3WbjWB@$->cDmoyf13w%Vri)KJIm|v7byPTfY&iN5F^+RXv(-Uoh~j}0&J(lw zG~e>tRCNycMoU$kCTh)w;W;OBMso7J!=7s9NI+b{F%P@{wUw%BF^5JBzWJ}MSOs?P zdZkarvox->c1e0ZH9q^4@u%Ve<8x0Nw_sS&2kdR+bz-l;Mt*8;=L$cKqxRdd-DVtG z%Vz9BUgz-cR%qee4Vi6?GiJ>XkJ$6bZhJj85D8`W-$LOfkBK#-Ha`Sn;$qU7@==$5yUfKAkla<`sQvM|~e~eOYbD z)Ci5p*8Kt1-7%_x;dWpn(9S2^PUih=OT!j|?L#?RhoP1&IRXSum;WNYDei`@AnIo1 zN+t1TWu@C?sjRfP+)ZL4gtfBD>8`BA=>zFugY>zWnmJ3Xqjl)i^so6ek)Fk*&*iq^ zXV^{qV(3{jyuORkXA4Oo&aK@cxKG@!nWhxM)?8d{H|7_c-F9_;p4HNAvsvm)I(v1f z##|=DFIMH{6|3^(!l=oqDK6K+@s|-BY89>W3*a|a5iH;E1d`?qA8=0E>WoEewbhY` z?VdBDTG@T?9e3ROt=n(^)(0{z(1!I-ktVy78^A_sp^(L+NL} z-b153oL(uN(Fr=s)tb}KRczf-p*&~Yy2S{OzzsV$6aJ$YG-Dl<@uCsKGIAK94ZP2f zpC>Ln)uR^us+sNWi^p%U*kaN4j4#{~k5*Y@@o^`wO8Y~#>6?a^cyA3Po5!^zUCzuB z!0Bj7IUOSwtCu#qmJa*QD)N`a`>J+~m4VM{ht7^bc=q&J%{xAr48+RZ6m(-QA@VcZ zIV#(zNhmyGbBE$SsaIZ}jjb8nM3FWmNA=QxI8R=N zT@vgL3Es)agpD!&l?T)PYy@*-nu%?`qO7s+HIv(ma>{Ev$>=+)Ku@#nIcsU8ht^5 zR@5~31OB?&X{mb0Ic96TuijPf>vuQwMI+VznX~j|IH1y&2w9^lu$32H(SmBQl;S;*VwzE;Q{;|PJn|0)Uqu|6kRyXf%DWcbHhgR{ z#n9D;2kqU4DQoZJ^OL5`;Yvq^(0hwaMTLd}-{SEDxwDihXZQ*V3kvbTd)gE}RB`ip zOUDA@Im;q^1#)&-=0FbnB2o5B!1>JX;og;N)~rE@#oATaMcgkE>3e(u->07f$R5gw zhnKQ`I#w43NEv+=y4`N^OVOF$nVus0(odsYo${Jk1bg2^8cXYij5jen^=3s4!<^fA z(N^P#nU1DCqxaFoq8|=eEVUiA)eZ9!^|gI|y~F2i-C(meG@8sMb>+R$cAvLrUZAEX z6c~pk$N6I5dQ0i7T1#bD#T>Ug+3zzO>)TU)Uw+BjhRRBl*`W_%_oZ#1J~B6CtxAQ0 zcI*{`<_i$>IiB}eFvo}?9UazIF+Qi8LrR+4amjgz66}>rx=e18Z{ofFj!f^dn8;|> z(4v2cBPK1>Eaz^SP1xy!3JjN*o2E|Nc;?h8Q>UB>H1zeZUE9~2!8_e;37gGP6S9|- zR+LxhE9#r;>gwx~ZQ|OY7p6`-W2k&z?^BhYYI{S_YE_B+!s5!}{6c3Pk#)mMVWkRaLd2+-f!853s!0WUg_Tpu{zfMzL9R8Pczhuc}J_Plq|r zQemkL25Ze`qt@KvwNzQEUcw9fGVqxiqtR$^R+~+YxEp{y@NbGW^3zyDm|=$lBq14! zVbMmf-8|7q=8P8xIciQ;_(dE`(KQ2IT?6zfo!8Pbk3Lgd^P{CD=3ZS*DzCPsrB=3P z3Ji2*p3l2&PS~U?d1B6-Yu&xzKm0qaFXrLS1=icJUq&$xJ8H*kX@gzHeTNF^Ir$l; zZ_F6w=T2H4%Ii*8>QPsFj7;U&a+ak5_2UW&`W?OlA zUa{V8Fc=Gps*EPP#Za!PFih}Ss>DBf%F9yqjYdOlOS#@=G3cxER0S1v)y4|FzP#5$ z-H?7H-ViOBa|@Dq(NcMBrMw7$Q=E2Xw!EEVy1ei!$KeckfwUU;3`X`oGq=EA3d}Sd z;pG`aaMWYvj|h|=6*N7xG4Pc!V|Sl4=HiR(wh4x#rVS%D3{D+(CZ8ZXmJ@zI zF=~47m0bM2EQJxD`zy?CDv~kmfzn^Lx29I17r*i)HqK78oH?hvU8Vi!-&NYC=!{v* z(@QgQHbJv2p*>z5&me|^r(i=N?F9<0fg)1=*a4SR#;7;TC}SlqOqX(YwnU9St2 z)xu9G%Cpr~IhOj0ePNxkQk$pE^I9A<{@;ZU`!?IItj&5GW+!V%$o>Xle<#XYSy!wy z^21hP$n<7+bwq1_BGFHulJtjvfz|DxkL=Cl_1|Z=AI)TSKnD}Jb@1bytwn?_JnW30 zEq+Xnw$a6yakrn&INj)F9?ne_F*R#z%lYJG*wR9iRTP-%aVM!4*Q8&!J@r$A;hJkk z7{=q}e@p+Zy}f)p`bHa{)3bG9ZW*@O;3T>Uw~nFBVJEv0d;Wjp%tn0Y% z2D9i2F6Whsm% z44zS;&2YyvR|44m&2oxLWMUpfWhTn%)_S_r)0XmvGSOJo)&7?1$`V~MmbaO3tmdX! z-;q~Skdb*}x?+ z|Kf@ZMAfM1B~{+VrV9tlMg%eC>Nx+@bv#&cmO(g1*Ri7FY{Q`L6m^`5`M;QF6!IC& zhM^cL@#Mu9V~zUMsCwkTAam8KLC1)ihWpgc=a=Z^*z5EW^}jRbiv2sI+Eh7CUcdkI z^N|GPWqF!eA6C(B<*9tmVJ`n9XO)JWPcdP1o1+(sgX)pZL7y{Ycn<6f?KESqA8BE- zeaswu=A2WL!D$dzcx^T>eU1;cw!5sGagC5E<(J8^>BKZ_x8&-ZuE)C*~lv_ zE5uoUhAdJ{+G!kIk*vr{7RkQemqs zE3J=@1^G9SM#IQxvx19^7yf*B zPcEwz!+~eH_b-G_oa+6R#vL8J{MDLCsQ(ueL5brfWj zp?J6!(uGVvGO`FcIh7=is&~;z!gydr8z&0l$hKtMAC{ymnP;?yB%Wc$z7nh|^ybV4 z1+d|djGZA#+NUC$F{11~!`T&9uPSREa%FKJS(1*b$TrYy&X$4pL5^N@iD~uf>E2;8 zz;CD!;VVfmX7^`s@-RIA53l%$vKLS4YK!-TJsGGP*C|U55KS?Re4b36D?+ZY)E*T! zM(4lSB*?@vHZ~4LGhTlAP&{M$QD6GuOh}_>@QHdvHOkUdij4!k7GBMdxvNMtbKG3P zn6~Opil{rKiTZeCm>syzadtdb23`|_t2uvsjA%Qtjb?+3P7%BI)GY|P;}K4uKX%+* zu1y;Af4NP?fZ5~9G20|bNuT%iZC(%AF|uW3>G_(T%4VKyMCq9A>R8v(8^>xn*VjoH zQ=imK7G$iw;?cX-xzGVJ)bN`iOo=fE`?E30Ydm?5N>3e&*1W7NWNd_+z8m9IR^sb; zraDQR#P`P}aI_ssXAd+o6plQwh>&$q{Yp3*9dZ&*u}(%gZ33GwTC)+? zu(WlEfZ__HsyM1OYLdpF$^%I-QT3FC_uU>NRqN8Y_897$qJQ`?FRw+9VrUA#;&8__n} z#k!t5uGM0LUlZW1%y9zfC)^$UaBW9A-e z=yVQ6m}=ZpC1N$Sb&MC|+D>LEcCoVw?w3bPe5UnTJc(%$8%%LL$FH5j%ri!2W= zA9=L+m#FuY3J(WWGlMXi9&GaArehqv2Y?$?qf3KM<3)Pou(KeEKR`R%>KczDc|rULIb#vefHw z;|ecw8$?L0ZvQ(AV>YG+vs46Rzg(|5F{dPGCXIM5*H<;fNyPZ#Q9+|VqN3pJX8{31i4W1J(exGvmO zs4^55mdR!1dNn9XHt4LyGI}7ZO3O-AB_$<68B7xRt0~UY2)RHdWQUN|a-Lf_aJwmQ zMY%ffxGV~%6_(|f#07elr&Z@^i!~Z`AJ%jDQ=V^XG;K5rr>>^dQ)?76k7iyhaqtO(XmZ?OE zQKQkR%3TxOdW&CJN`ahC$=NR|Md$w^ps!I`Xxuyh3%p zMidEkQGptVl(I03%pHM>XqCRwj3Z2Kd9fkrEvYJ*XtG$E0_Af2^k`5v)K_Q&u}VvE zRn$`1WQ9zGjSWV9wHxE9D-achmOQiC(qt*GGniykgQ>2vtIBGvT2K(JXfW1J!6lqh zEGO)pH4F7VzsY2ji;DAArUs)Cmm`GBU1%(-sCQNu`w9wtO)HyZ9ICiT2QihSp zLpJ127I~VYLbWPiHk4pU)sWKyjaF6{V=PrI<+TPwZMj#Yt;{bn8)OVW&T28}>O#4{ z5MKxo6vbsa{H8M7EO|PsBVVP#NbB+|@>H7z_?xFq|MvvBOj}fRZiSGwI-Oh`hPdGh ztxBs-`tu<}1)@+DRLQci2$8R^YlNh#ii-8cMdg}8G>9=NRYPCowmh=%NXYmalDu-q z6N0i~5I}Wxo=2n0FNC7XnmkoqK8V-p3-W}4a&Q|_=u|rSw1$`l4d$s(2Q<7M{h+R( z3etC#Y=G{GBCnA14yU)`lE|OEszOcaH&B4e(MuW)^i(Zt^7GNlf>M>HKrS}qnd@=e zPNQ|pg<7?$KwYKIM@bN=hM=kP?a=<>(!y9ndOlw;5of9Nr6sE33c1`X8_NxPjiuU} zAJk~uWJ`W+fliID)xZF1i!=pADviL{lxfr@8l$SxnzRaA(rOeYE!06(nLkdzAe3k< zqM}SM7nxKRwO)-2otgVA+x4Z|q7txBlxp*}=r~9>Y0C6!RhdD9LabhJFU*-ko}w^$1dhykCRXXMZK(4tI18SXyLN)N*1KR` z_GZkYsp67N-%nrLR^xc;mZf6N)2FqWQ!xaJ%%3(RlB!HKAqmgRC)<}?F!wi$y3uHXX6iOZ-K&aJ=&Vg$uC74d;=Q`Z)U>_Xj8#EBB%hX;^DhbC7BZ# zEMsZaiqofzJY}!r(e0<}yrZu_iPL8;e`GN6c%@b~h*l?$0_ZYWB z=&DKDKjtNo|5JOLOxIc7ICXnCA-c}|FSJLof|*;=IcfgN9-hHsea5NV%tjfe>&NJ2 zu8%QDhfhk9bRlLI<^_HtoKg7a@DyQuI&o+0s~LT_&C*z7FD=P8lyuvyHFecmUB0!^ zRaMq;YRV87Wy>omgh57BRGzF=BQ9!ia11iB=b0IMx;J6Kx_*o`>(>!E_av44at!LS z_b;;10BKyq7`hZP@+wh|F8UrQ8{(R<*)?h}_C%(s=+r%X^m9T?e_!qZ?(z{Nolajp zM4$79qnG5|S4gF`BdL99e-#s#RiDIrMluD$bUlDm^i$8eUrW!x$=d$aw4D$Gl>VPFX_WEZ zmG~Y(mWdJj*%w@EzNW&L8CFaz_(C=Ok?|dulJwz;Wj{1uXDRayO$xcPxCe^+am4#k zd*5HOm7nFyu@45F-cTujMfsf^>!jzC-J!GZ%-(l2Lf*;Fk(|(2ovERVVooO>k$KCt zPj>czdP8kp~$x7IRlaV9Vm z=pD8Puiid?()gCDWNj(F>>^o}=)bf(6NfqaN)-4u4kLdh$KF!3$Jjfnr@AXP<}RvX z_!ilzVsi%vPrO@}nIjMI_|bVA>jQt|U+&O0=bC3t6>gZh$?X{9mRUwT{yyeiv;Rwb z>*02MJVtvN5&Qb~NaiZ|PJ71hps0nhdzf)MHjU9{PE0=^qnEipQe3q+>vxP1Tm3bh z{_dCqJ#`G)PBrT3mrqS-W_~}AjU{x^x5k}v1@P6pS~ybu_`4(r$D-{7zvH7*lZ82{ z1>fG75*$GV1VU9l16$%B*dFUtAID=1=5+jMsT=>DI zz>{tpl8=$;cV^C`Id^CTN1tJyNO@~+Oow6#o*&WXi4)o{Pt|q-&VzFM4=$Wt^++?M z0cpLoiM|(iti1>o)(F|*H)I2U1J^aq(A8oO*v_2XAZO z8#I>w0rz2Or(=TEWamILi&m9$nbQyFJnEruUp>Y5fCh&m+D^GgBDQBYM+OGc`^qvK zCe70fRI^1J%X-{j7vq-Bg}Bk2M)!zwclop4I3zNv&vI>q%WD!}d5>7!`OifsUL;m! z?u=&b743+eLXONFH;%iclTQ+!53;vQPY|5`;k%|SxJOf*yDNilsw(C$<;$1i%6PdO z`vMfdV8>cA&fgw8n{JC!RIh484BW_X$o=;N9JSCnLEob{<)q>L7lRmseEEgHhEGG`_=Dw1u*BOva$cU$hAz2UuF$9M!4;%g6|Of|F!YACr01ZR+`2P#T^}fhqYra zHkc!^hj*s_wcy}?g}O|1@K@1{A&6eb*nM(23^gQtk*P;JweJUQ0?Uo@E~T?zO)I|)4?O^~kmL=whJmP=Bx;~m_YwJLLgHsxcW zU3m{!t-J_yDsKY)oF7B{SC!PE90ZP2_5#N%`+!{v)zGaR1@_>ZbE~928pVK z%I|<{aNoX4+RXKAVSE|iU|uD?DlNkKKo!2j(JEQf{|&S$vw(JG03~W@^0R#Lr$mx)>5jjnq4s(8l(@|wD@?)G6SI$8?!TCw$ z+ekO_(-wZ3;`~-_y^YiDT)qQQ>Q;RJ0M#>&TOY4nigXv#)6MVpaQO+!Mab_}ZUpuz zA>c&ia^R%&&A@(TI&d;Sox(LtRlbRwY04Vlbmd#X8C>T~E;EbUovoaQ{5gzsm9vqa z$CS)x+7>V_2_$44Vv{UU?-Ox$L)?+ev6!L zWk0Y-c>&m~yaeo1o&io&UIR`_F9DL4*&s{Q;#4j(P5C9#(-peqK7(^+^3z$+T$?mo zc@;Q^aW2<0kK3KkxPWmXmtVy1E>_+^`2o&f0-_W(^X)LFk0|fstv8fcfJd3D$C&5GIsa|+!VamJ1hj!RJEUR{ zuo{wJhg9qYIzgKq^6*EX8};BjuF$$2vhgV}0E=hG_q|d&1U}%~);S&K{0OI`$|uN= zah-9lEy4LoP;Q6i`3Ts;FEH+?2r&@Yn*ZnI38TG!}7cg>_*@1 zkdsG&6ELQBNDEQhr#uFn$n8!_Qy=@m6FW{&4+Ez%PUF^RV3h6BEN*c&^I;C-T*!T%B5&gmWe)*hyLFPDFXsXEBz4=Jyswxdk*G49229*ehu)fnG( zKpSYT#`sc>6C+!V(cB00Vd^I>sZ*}w29_0h1`%r&1xJvnx z(l-M8x#SE`SPhFxwXFi3)#&d>z|EYq1y}=0Rsn6w9Y8yHUIR({5LknEYe3cSfo_aZ z4XFAJFa}wz!8ha4yB*4}f#Wc;HK1)bunWAc0d0gmkbxSEJJr(%DXjs8RAy589ALkq z0j}n!o4MvKj0ZrW160xfEM!ye0ou`u161t-Ix&I{(6bll=aMnT4$LDCQ1TFPyz&UJ zi{I*2&PBQh?K(ioi@-ieqXU$D0GyP*5ZIsoFW_o$&jCr;3Ea$eZecut-Z|kF90FQl z+nu25KY(`SUSM^4AFxKb7U;lS3PG}{in;65&_mGZo zI?DCOxc&s^B$e;rX)~yGLKErT6z8`pw<6uf>2`b{fD<}+5ZD75aH4nA?nFqn6TQ0z z*bmM-VU7L3sfrUgO@Z$MYa9g5!2hS@lxFhNSzPCA7cUbzirb|?+No!ss&%%@IR<7L1XAiYk^+SIR?AOlWVW9rvFWgc)p zd^Y@FYw$pv(gDn9PR!Tu0S{vCc1o{v`9oauVeZ8d_!~}G=EcCHn6sSHF>d9!av{=h z16`1T4xmlh4zy!Na6vME4XlAox*(sg0$msd7i3^J&=1Sw;#u1T8Q6t1&Dt(l=9hsq zYh%Zl^J&(0;k(FR1jaz8i)U>Y&)P0%+&<(qLq1)Q2danWZWq4C^Ftub-7Z+>SAZSL zQ@~DceH{4df<_z!c42&7(9P$7J&;ltG~&0wKILiPMD7cHX_*T$L-kMNwr0RHalsB# z{j<4`bD-BQX)gD49&DEjc9`m4z_R94Rm9?rUGd- z;l_Bq4Xjq~2G)RnH^%D#&;@Je#z;K~j6v_*;P$&f;-?$@dHS!Y)wm1}BdJ zX^wM)hsS}F(iZ`Vdv0jlVc=A7%neRbZ8JG%A^78logvybbBkLT$?I@K&Z&O#I^5DO z@X*a(hZ}PK8q#~XwwE}6FVnV<)BE}9%UHj-A=T8QLtGE}8*a$+0Yu_dVXXb&Mjep2 z;RiQ<39Lr%{NTo~feuja2RGgVx|KVDjiA;KxqTWK(;QdVbR<2y-d|U&fgDt_QN6(Z3o~d`!QaB03PDn4s-b2S-CpDt^6429)7nM z+SCYczXqHLzBPi|8-e{?W(GecPrDJi{0eXdc-{zI-Va>GxSHQx#}sbn*0(SoK#u~D zz_)-_r3GkH?gQE}*90(!?f{ZL1|Wg&0-b0p06E+TbYr{%=*ug>MotId6$BuSlpn%~ z1Ykue9mc#Iz+Kc z#1hTZxQ6Nc?hIJ$0OXd+%;L6YGsosI&Sm=NF>Ujiwgrp}x%?ugd@9A=IvId zd>iLH&fMD$83$|}`8y#40cZq`!fwuafuE9}9Dqhp-)Y4Zz#L6)?dS9>(2D>x z;-|n@x!2@H2cR8P<`CC&m}@@5HPh-T0PUdGkFm5IhyDgIN56;|CaYot+LUcTJ9In< z?RXYggVy04V239N>G}ldM}LEmtNp+rV+gGTAyJeQVT?i!gV3YU9+sMbx5cc0f;NH_fJ0vFr?sWreU~NLs(6@n3tO7#d;TE79k{<#m z-vc&+&JgAcdK%<(i0f?PbeQuaoQ`4~L*VM0z&Pg95O_=PCc&!^cuP-PxO|H9TOqe0 z%pg>z9b*~-pAP~%x#T#ENCzv7DNJB$BE+HP55XR*#Ji#8#2}e z=~@M}DOUmQ*uiUpbj<|TVBT+nbo~KHtIj4!*H)k#8q@^ox(wKe-ZeqGs7#R4A;?}6 zw3YH{f1?RHOX(>1+yv>G2aLl*Zh~}CeiHp{f^^YS+TUn`{!%{eZ!|%=DBZ672uN#N z{4aIz>zbg&JAvadMoo||daIkU2l~|n`%h1M(Z?o8*B0PJ%n?n{=(~aa>E8h-L;IQ_ zT`vNs!tysky6y)~hkQ0cy7mBPa+z6>hbBlDQ8(nUG*xi1SC7jpSUuoX>^ zuGfGAoWF!?SPCg^g8e6Im-EvVuqREBE^2W#w5bU;pW5Bbyxqb`J04B&w}~6uxJBCW zXoBZMe0UyKvsL3n!EGJocaOnVG(n<>OK(Gu!jPyUpiQ|6Xot>*AyGdA z)}Y-mWa$Q=3w}`;vh)Ga56*`nOTPyOFs5P15~V}vX&ACZ=`iOkfn9NPS~6*H#5h!Fy7BS z-O7F3#(jUBbGCE4&%<_wAt9dtcXE0cQ?eUAVHh^&1>j4JdpTzxr%5)#urtKT1Dx|J zrw=m6Ugvra!8Z;|hdE6iav19x$~nrEkcS+GRD1+{8=Q|oDlP}wkRO3myaBAn_(mWV zuLE7^V+1nrGBC&(g1$sB`jiu4j6%O67;DOD=5&hFZHyfl{RqaC%JgtfAFO=@BQ+h^ z56O&RjP?O%amjgHay9rIfqqe2+c;-CBiYyp>?-Ax<&40qp?1k~M$n_z@$F|;>@P*p zqoY7Oo<`B5cY!sqHc`m=Yd{yc7e&8z0t1{Af18uN%F>v@8uo`7z;OZfu9~8#G+k?O$ zV-sV9F^YC$kQRDM>x&p>7D~4bgEgjl=+rWX-ckA(%Ez(7+6%N|CXJ&Pl%`$EIC}AYAe|`1F~hzE zq#dd_?BP~m02VS1sUW0Pd>qz{(ychXit`>w92V>)q&p#9ag6d|U>|rAhjqFVI4OM{ zupbsOj+yQ#a4M*Zqeo8wr(+(8!&2=9&g7>H85d)QjbpE#TBO-3j@jx1;BuyR1$JrT zn60(~*DLwJ`*A)PhlfM7ZD&fJ2RGudHbmP_PVZve4QYwP+8hAxVa~k7xR>+yahld0 zaafy=fUomghq#`@oIb+%2A4m|bRNT;6^A~)1AH4jN`TwDfL5g(Xv5P4xcwtwwbBNx z!OWEaw+{hnmni|B?*zJ)6+k+#NkEqV2n^tyF#(C9bO=(BfSyu13~Qf&EuwT3GMvEZ z>;=Xl2?iQ4VC>EUc0xN6(9;3ncsg?QQY63G6;X*FI2&0gI_wo*Kfb*9?5)zPy-vO5~F30|D0`t_(z*X4& zPQZI1KCFXnPe3*{0_oHy0UaT{pSiJ>Ik}D7eVlW)GY6h$?(N{GJ2Acq_%K8d$xH$= zL+L$C)k}_}QUhy6vo%JLjKSckl+~0$YuXFiBT>oKClPo1LTT%T-VOtW= z*2BQ#oPHaagcLOcZRmRvQgjGNvsIF3t0W|6FVb#)8bGg;kPX5X##Zn@iLs`)IgtmSHB#$Bq9ef|S0$x@U zJbw?k9z9Bex0HSy^G*^vxEpu?1Onz8CH~@wlQ{qwr2EhKd^^$`nde0^bF+mbGy^{ty!E; z>*Z$1-ag=JPH*KJwsD#57{_LeBh_}0DWMf{Gx|a;(u%klntljT)N~rvf_^;@v}0Y| zf`07>)__Yb(C(iBU3jYndc6l2!^)@y{^3qw3v5~oMu(72sah~DROficF08g%Fggzc zd$4!Zf-(9S*atdWFgm9L`>`5r!RWjUTnB!(K-#I5Ex;6LYXj0raSF6;2UaUM8vt#0 z0UfZDDNy(v&;>b3L5f}j`mqX2VJtoZ1~B`iFd~!=!EU55B9snueuUFetSnOK*GgcV z+e&bL5F(wV6juMNK|GP z^e6@2_*38<#<|Rsc}&TC=GX$pg|L1pX%X{kF>G%N7K!+|1Ut_uSfmet%OC?OXb1IZ z1-HHuI+B8p5LZ`Y98=g&I}BXQW!7n+0(W8bQ?OB6fG=QvOus;m#*LE0o zJ0%@~?4@9lh}%b*x5uE%Dabr=o=&J+Ay)~Y4Vuylx!MV=##^nBt3LppsHYVY^%~HR z8d@P6PXH-?pcNzjEU*Ksn^s8saUhL#E5>>|up2#U#aQ11>;<HYy%|=fi_Up21~N4e~%#ErTqzu_UxX z5~$=Vu7P3++F&iIp68))ZSYTC2kzwbE{u2^#_0s^uIDKC_ZY^y4I@bPzYQDG4!+F++OW!Q2jAWT(wx!` zz8wP64qiL>_Bzmwd88fV_z|!XeQAeHH~FGG8VLZ6k4taPV*o_{wLmu`5d%1icq_G`Z{}bS(^m<@Fx%%pU z_kG^??(cm+$*WWTBaCaRn`YNX7~PcC9@-Jwk#Y*m_l_{ii#M&&j?ijK;D(>sBpqTl zSOm8*(+I)&gWwMKdqZ%38r;P^IfSi#0o+3goFUUjLNNarxR;r22sf<@U|#7P zC_hARtn_~8-{|xK>^21ddh#Z}_h#oGV)r9NZ>+!G>c8IR{KKx*o1H$w>@Gy>mvDVnjL5UoRhZJ{NG(8axAo3S0e zgcx~MekXo$A!dETZa;tA&zvwzJbQ7~ytS>Ci6)A$}jkK$+C=?O;O5H0aOFyr);C7m|Tuy+)qB}&GebLL#;yweND zym?;4cQJ%LB*7(4Z9>??d%zX{w&?OpE`QZ!t~tHVs1(9l#NMXUTVMs&@_ukTt+)bf zDS|t(?Fy`=0Pb;)W=$1X$TOhMDJrlHq0T8PunnbkPEo-cVIGucYz3`SIft;c3hW^Z z>YSp25m~6YVg)v!l4S!G*uZ&EClM8BzX{Y1Mgy@QEig(gcI_FpxN2(zyF z2e8NaC!OER$P&iF9|!x!?X3`g%7gU(-tT^&K$IjG_{K zeFkhomP+(>89e5{wlY3eqAxw)>H2gTyZzVWjG>k2YZUBp{z<$7D(R7)0sCC%e(bFh zeXW3lw3kZsRRV{ZjZ~tqBABpTNn^@Vj2S23trC4Dzzmj9iN3_iG#aTyU*7_=u78e^ zu9CBuhroH~FZg-w^Hid#4RFQJ6kS@$e_b_KYc6e_(W(+nNjsagph{ZSL{NpMo&vWq zSFA!)bKnkEA5~~-3fu*&RcPumC{0zNsb%nbn5;ra!ds1psaq9#QE3%0P=#KKU>)?XQ4C@K6QA z9|2RY!oz6I8o-wDQOvk^4)4lqtX6G2m*U=yB`5j6D_ zc+7v5M@@uL{zqV&QQkEXT9fM5W$gBo#~Ixsw5E4~JSg5 zILJ&Of|frC4%5#^(DmJ5!Yn0?DN8!)H_G=Q!k8ktCYT9CXj!Mh40}NlTGpq)X}s$r zX#Wl{Yq@gfZI0d12-??Q7hLipvyKQoVIEw<8Y8r_6u9E&i}YiB+0P}fGE0fjAO1PG zj)h0ChdaSdMym++a3A+#HGW00hdaRSuouN165!5q5!{7sN3n->P`f%&>|qkr+%JkH zd=AvyFY38p)N{Wmt?zHh(cCYJJ>qRK0i_H;Zd-T79GVN{sPo0EQ&qo`8c)_#U363o3JZx{W9x|Vh{SOW(85~ z;YCm@>nJk>J)>D)6swp5yNum_{g80{NaFjk)&uBL| ziY4f`i}2U)g`Z)TlKe5EMX1Qo?aA7ILCKAWeMCIy3z=4 zr^l|w5*`9~!e=#>Pylz+hg5q-P;Gmt#vYy|XD=2~ja6j9>uGn@ScTH^8m-1El-}=r zd5u+$YEq3=EQ7l1QO$U&^5soYjcuqLd5uuCUCkP871XS$nl;)5u#Oo^HP-Slc+@#@*Sry%sm59^g6ci^zA1fdHP%uDTaD^B zs<9BEyhf{87pTk={*`=3tFaB$Tppy=*oNNT&)m2g+jtg~*I6|dqMTt%cLqDDrpFL( zNn;9YthVoHHP#~D#$2*)c~oO9s@tTW%s4$|iKdM+%2++Z8aok(+G(lAP8NxIzU>O@UTk9x+(|pC!4i}& z&$k-1{~V~jxEeGl)ZSVRZTSJPg7?;-uaAM1wD%e`^4rtf%j- zrQ`)LAG|fF#CcKW?Kp8P7flm502o1X&rAa5NqE`xjN`RlOTd%;8SR);mNf!Zmp zL#`rN>6|LY#5$z=4j5&(ybc*Jg4#Q(L%Mk|<~JT?1yo1h^(5Fxi>t#LFM-FL-;K@G zVQH7a)5utd&8QAZ;{?*xVHK00ym0I2LxegVsADwIyRvAr&V5K7eMpwHym0HV3O%W_ zq&j918L;HiR_Rab7&R8a4f>+`;PvH?gWJmE;P!GaxTAaw+*y7bxU2j@aCi9w;GXh_ z!K=&N;5Fs^bP*)n_TjK=ili318A)thW`M($>rQ!ULt*{ z{1Nb$awB-FpTEt|A1)7&^Jb@yuqLVxLgjnF3g?8G-PH$`&Z#QDmvqD!Wyhc%jRas_ z`7Br;e1`vHSN;S%ieGen&`=%*Tmjf^$*L6ggAzVb(;TU<^nvx54d-RTb3ywi2= zDu06fZkKa{GmHA5r~E7MWceJ}%dLd^ps)NO*l+d*%-&#ml$;@akLrVA^K+UTo%O*P zGnp`xNn^?kr2WjOIT>@#xTT)(dnb(<KR|I1q+sU ziIegAVA*o5xP+pyWL!0_8P_e>hH=xl1;((S{|jzs=OIQbyA#}5eiYorzFCZsB?;~+ z&w^LetH-dMP4GHeS&UvqxX*Y4_8enOQO3UXn;4^Gj48@L#0o3M zn4%|db9d?SYCJONfXC(IfoMq8c&tC-2hXv?1kqvfB1)wJap_EQ3D znccoRux`Qun@EI3jA7qG|qC(FMktyAe3c2xxX%l`@vIDfEwh4fJQH{ftt zC66%b#n|(gbf=j?#jr7zmN4f@W6C_F&Gx8kGUl9d<3#y6o}Vo1jTu%_F?yb#fzzy@ zWAr@l1+$hqSALfCTzM6oXB3Fh^T-DB^er)LOtLJqry2`Z%FSv<8zc2@eJQU+eTl}zds>W?|lN)sL{ZvF%4erv~I~YFm{{;uXB!W)-+(x!W*!h2JBgB-K=TAo`nankOq3f z5_l86bOZLR{6k3EfLs?r-K=R~jwIC0ng;AyX`Q4uAn!XsdAu|ruj;8Ww*h;e19hI> zz}WOKsPps&7gRaHL%5Xldo?B7P|!M+`Ivcy$tpm z`~8*y+H!;EKMjo9Vo7)C8ep{uCR{?&m~zc^d$9qwCCeD&U;~{063m+c&0!kgUv-dg zX9Jd_w`;D_;JHczwoxFhlh8Oi`h9Ra?~S9QAAmbq<;Bs_BB*@>Zrkv#INH(k*U|do z^fq(gKBsS>4so=j{Qb_q(dh$>#c{Ng2X8{xakQgn*DC;hhRPZa~zF40Um{`I6ce~7D)YeR`bV=d?}|Ov<+*&fH8qq%DiQ@8alGIpePFgx@u3%oueuB~BkAoH1ry!<;3W zGtL_qjCsqlXe_wSOHRwTACf2x>jmgf%XJ2a%`=o4Ejns8tiz@>Q^sxn&d9qF+Ur%{E~z z=fE0vQJSdRS+I^4%AAWCQ4`jplADN^+Jsfig1yMn#Eg0#958By(u7WPpuGN?XrY%u zd2=xk>YXiY9wuT{hM9?X8NmV$T!fT zNl#)k&5TNyz$uqJ?evV(S$J+{KAZvP{QSJr3$EcyGU}F7GnS)rN=~o(w`-Jh3gFs0=u z+`{;x^Z~Tl;vTGp9xO-tX6N5RPt`&XrYCQ6jy!~0=)v?WjVvw5^;WQgp1B3NJ_lB^ z*VKYdmcR%nxh?2gzp93Z79{;HSnJwqro^na{2?$_mK5@fXhHkWfjYBjVSLfMbbqsj z{z}iw3!()ZcmdQrrv>X34!C^zGqlhPi7VX$YQY|a^8Rgc|I-4)=Sh#DwHEh3E!c^k zmw!|X?Nm%^FQtW%MekKSgcfW-5~;6fp|8+y)mOB5E!@hScAkhuIuUC{M>%jO<+P$B zrFA2$6^$%|^1y6G3qpBdw!;5eP#&1AwwYGgR$3mIt?--&L)>I(h36$OjFwyB`R8DS z*?21qt9;!PX@y(%wrE-RfiYTTD}3r#+Q(>xH@! z+mL+;)LDERva2+m!ne_CW}{64&9b*y_BPAjhMg?(SM9pAA-mqS==3t$Z!-gJW}poQ^rRSQ zr-l9*sD7Xw1|9@=a{AT|1CN5cd1E^~JPuxiJ-5>;e-7?99;D{&v@+$$Bd#5JXTcB} zY)9TzP`>x=$oma2!j42cl0E{~(&M$$!xzE&vi=$?Uj~nIQo!87r8Uyx+Sy5X7Sy>? zJAJAC+HE{R8*g_zYG=19Lt3q=oz}Djsx`G^4+T)?dhPI|dXD;yW7uIkEa`c9C$`g$ zRCDcNwtN4foe^BW(jI0z`w!>9oYQlbTHcB6tO8a*d9=1OQmdRIEwSA@mhJ3Vst#*@ zQahII@V^FbI=w}GI^h3qP#)$T@Sg{FI$y1i`}CC10sqsWW_KM}V+PbIUI#X*wEWDu zbK~@W=gZH$1N)o=<)70*->8!1XWoIWDqrV)9ayY#4m&MB^A1~V2O88HHS6v`gC#KH za^z>;L4T;4)cUXTGw;A+*T6#yaO8+%Fnz58&+C= z<{en_9H`k>hi6|MXifD@(+75-yG!7hpC9+Tgzf@ zFY1Kt*9V=j{S3GR-F3qDEU5KPCp<5Ly2;xK&-36u;|jBrn?leX|9u$FaPCv71Q*0U4RNn6nK&G66( zpUQ7_-P)XPcYcS<*G-d7_!R1q!sA5TCH??F4PH=l4aDtm4`s5S0$b9 zCp)p^C&*dyyVP%VnXxVydz!Qu>w>WvP*HKZU@Q-6?CvsSU1qEcn_MDCjCGl@F065m zv>3w!+WBIv3k%dUVyp|>Q;rzxg0U6Su`hyJQ*^=D!=M=Jg0V|refeHcjCH}7 z$`NB-W~>VfR9cL6VSy@NjCGl@E;H5zW0FX#ye=4<26d~t3&vEw80#`)UGx~LmDX`x zW~|GMb(yg)EU?7=fNfXygWKu5x?${DP`<=HO&OZTnz`${8tJrMUFq1j%dEjyC`2guKyTQk)=fhwX zj2)+*_kdAmHOHxE4y7@M90wYuzKEg;nDvWKxrSYXE3!`P(vYKOds zu}QdURK$am@OCSxcxWf#Z3?^wO`U{|&x2w1Qcl9dPe4VtJBjQ|U@ew>68#pzSoznW z`k9l+B9xDMFD2gyZYSnIFC{+!>Ljigjm&@=-Fnf;Cb*Xow%4OoFB-W-TD?Ipb@((G zhO1ucunJZ&*7Q=72SJTTz0~IdSnK?H)-}D1M@jIgf2DY6tYTS<^`ZrpqsV5xZVSC| zuJWwIjLT7!iC)-N-EvD`>_g@pt)i{_MmqqjahWjPo498;qvS#T%b@&nXaxQD&R0o(Hcbsi;s5Ge+z^Lg+H8Xus}_k&@2<^k$_ z7ObLO3{dA!fl>dphW>wmI-dizS2p1GHGn-ANz0xGsJT!v{RXgSE#$ol}O_+W!b`T|&A{w5eJ{~M^5 zJBXxNP&YaU-Es$!RQZa1F^F8kjB(01ZPbb3Aadov0-7D9{>qUr_aOBbt{T_CA?nYa zH*|VCxC1Q=A;q`BUF--E>5AFf5VAZ8?qyvzgigN(9%5`7Lb{)UAuM4C=?b9YJ`JJM z&w~+0oFR1jaj*s%hmdp~tS4I65R(1|RLsU9B%K8tX-z|n7e(+G%nu>&S?~ls{1E!h zfeF_kX`JwHCyg1S;uj5}U*U{Vqr(vWmC97aydm@}TrjG?8iF6arD!Y}wQ?Ckzh{GC z_;~}koe_2ze!c_lgsWlr$$+}2IZR(9yv3+hmza5N~FMvm>?J#U?fsJrJ3>z0g`7;dD=jbinPRnm$7*1wEt?7p8ZS<}Q|5dwZ z!`Oy!$~bM5XWB5fp^|ehLAz(ea3WkV7U<1};Y3er_iPxO5w057K;pBa*%3Hd0(a2X zM&M)`l;w}GYR-c4l^yX6Y6Q!fBYiz1-3V)Eq1MhLtiqJmKHCVkv<22NPK{t8wP1`M zV+2lAS{(jI;6!h1a%s)a%lV1eS_^%0t z#O;W=9bpuBn)EXL+XxIxq9W~R1cvqes-IkQdfji_K&K;E4Ktel^H<6?7l) z488&X2i(Ew#u&0 zGxP)R10zPo1U`f1KM&TT>oZvX-Js@;XRzdF!Gudr8Vg92z@zDx;5H)WC6Fis>NZ@0 zx-EjckS>9}R^wsr3MHuLb6^PHxdd%<3k>7sg@-KOdI{=z z2Ux>Nae^A=!8&^B1oeCu7`sCBH0)6M+T~8L?syTD2WbMk686(CC9tdSfkTYu3FIn( zXBe*&%=1+rokAwCs4;MyHj==ura?t8NMKhp;FKkrHqO9sf;KC86~`cfUFp4=K^(K|pzX-m>$s5Fh9 zNi>xQ6{RgnFRFa`lqTs*mqE=Om?@*9BpMVdN?Q^Qej8Mjwj>&y0~Mt$i3U~kw9C{S zFNq#ULG86B(c>jhQQDH|@iM5huq5M=P^*=s$K@n?)L#{)Er}kLuPALv`UllbQQDGd z^CGAyZAtnEJ+G6mB%@LWRFt+P+B^>`N?Vft;l&_@HqU`t(WKDkH$X)^OW~(63F>qs zg*JZyD&koRoz8>u`%2NS7ePflBSsf(KZRzMR>U(R$2cuN;S~B+z9OEb(C-?kh-WFZ ztbEN4Qs`QLmA5eWHJyK$c|rlxk9Orh%^f{J*?EdexwXF3d|=r?kpBA%tt_`_f| zGvO3%Q&>x@OriJBfr_V;LhnBS74a;^Nrc|6h-WFZuM!mTjFT?rv?*-hJg8me6l2E} z*v2e3#SV$;uXsu+MibSo%czKFDR%0f0#7hnr5IE6S4BKaVL6Y0@)cyZ&WO)!7wb)N zR<4{O{NhrKEGck=mX+dk;|1_Ewv=+jvlKm^WYrz?~&r(>|L!cs_r5K5XnjfaH zG?kqRdb0nmZto}uA3rvrLnYcgCR7Y#?thpB6o2g0$WOBY0ra-+?B@C^nA7dsuP(s zmi9$Zk-O5iv^1mDBcv6%D~+WoUy-}gSlSmrMea&tY0rU*+?B@C&Vur?NMmU#zui)F zz-<~!(|fy&irkg9rKPbn$)(6$X)H~()g8k$mL}e`Uzf(xGN5KJX>9Bqc*^fR?YHQ@ zVH)ca+lt(k#=69hcJtC$*8(`^nvBx}q_M8=fr{Lf#=1s9Mea&tUEc>4xhsuz{TNi_ zt~A#5FsR5~X{_rl;!*Ude0iIX(o3iu-R`IFH>Ul} zC~bTcR`sOz=SN{fCF`dDC^AYS-Si(tMx`a=7&7hyb<>}?;EXS0$hZK?qkx#otir~S zbP?RkTgGSw4};nx8pF1q2j#gshE=OH-SHp8s(%RTj{g{PeF@Ya(HPR@LG7K6A>A0L zb@UiLlFCW2P8mZYp?aY)+sT;wpK{$QMXmO$-7Tmm&I+_7>b8s9T33YRD92xVV&Jo6uNbjv> zmWVei-jd_U@(Zw$-fSFA>6v!g`#74)g4*#O_m20tcf7~Zot{jv9v??up)@#-yca-e zkh0O>I2u%G@=zK_gOWn`2glK%a&&)i5@uctZl{-eWf%}X% zIR8yf-)KBQJtyheR=}H`a|`vEga`fV&Bi0VeG(q-0_8K$Jw-+z?piULOrq;ggYuT2 zMAv#!{NPiJb|zt|3yk4|HwjDgpuU4JNx!7JwK+$OaYE%1P9ysyoT&bzuF1IHxM-B` z)+DVv4=%d|`EE_ZjNY!()k&Bs1QRgx$DktBOu)=MxRcR&0%o${9^NLwTq zRzUgG5g8GwC$Jf%_d8!cbrV=l20ZBB%BOAu#*}{xErMtnep23a6N~~XP2O}9SkVHg zxHJ>6_aYdkALBk8tzZK7?gX`)Izj(m0&DzyEj``@94cQN;!j$>0Lq(v0uEJjtL18g z&k3w94|e!@d3sOK>#7cE%cAo;TDfx+Eoy@PR`1IDx4H>3LC>k@m;Cl+mn`pv32aYn z=tOS>Xg>)C*qA!CQ+>Dg@`cZ z9PvZ!3a53~iQE7FRfyApY4bA;KVzhIYQSALPV~6dM6IUbN9F4d)HM9$LGgnpFT26h z@UskxAN)aJX4-9Y+T#wV%C3n{SUAHOU_Xr zPHYvYb+Sp!26Qon{k#j*$>x;phg-RfcT?`WxSQp7>1<`n{BUo>Y4L*xF*^)X@S}1x z7EjrJ@EL)nDflUZI^UdPEV~41o-;+?rJCr}a>~q1VL7v;#mSU8nZjn2-gL?8&u7dD zUz?#n=SwEekw?%BoGgOkgs9cjfoQl!aYCF9bg>}M{TWuE^6D4&fiTs;rUBR30UO3NdcIG|3;BR5N1Q@%WMv-ItG zP#(ENy>h-ha*03WC*_fwrEmWQD34s?YA`d*!sIDX9=TbV{0b-^OYZo(o;p3}#-87+ zXg^t4{Sm01Dhr?Yfbz)AGPj=s<&lfWvFjs`T)a53oGf$u6;K|zc$_(39=TaKzYNMF z7cUI|N*=kKmK)`fn`Lf451#O^s_WSGkKLJkKC;HC9=%qmDV_wbw8ZNW|Wpk?i@Dr22kHInZsrt19#Hm z=CGOPL49Y9uNE-hCL%ua&S5hpP~R>g-mURQqjuVfP{Ii6Ar-7oPFatyRfA4G?+tc&w&Zck~HcbF*8TkWYH)e(K)y(208fI4Q}WB zj;|QeE9T%Q4{8;V^X!^0VDKxx3qij`q<<_V2TNx``Q{P**C^jSB28kwM3;nHqVYNB zkaM&rlY=EaBM-eCZRZ70C#1y5ppEBfI}d=Bu1OVo$-xpQD6sT6Si{O92TLEx4T>628BLMe!9Q-Z&3qFMxX(@%gR=?9Ic~FTn#?#XOu`0uMPY zo0*3J)hsa2nJ%$mVS)^R^T2|IzbsyAC_y9t<8A_%*w< z4wv7H2InpHyrt$G52@!Z^*n13y-O>vd8Aei6&HxLqiZGqhk4qdut1p$Zb5tr4ZZNy zDMkvuyu@D@koQ?|5A|F?-lxF5*eYL0pbkWoH>w2@HP)yWM08I-qZYJaHi$?Es|#*H z#JF<)5$Z#f9a<2vSf~}zJJ?+(DhB*4(1JF=2>a*Uct(#4?oqjs>9oeF1v9e%GaaPW zg7Aa$+tq?NTQT)8no!drEn<_EWG9R`c+4H5lU*DGxvTt=80d5c{`n zMtzG1zeU$Uyy0bL6mNO+mN##C^!O|}@^NB!$F+=7PZBDjNjQ30Nr!N9X}cEeHuwx0y|(hn2k*6BlNgy>f6 zOcH|!oe~4aPgbF$0-P*@+5_aaG2>kU9$pRVmT`eu!8E8FItBM~1?B^Kqt2=EG-S?G zfB|8*bB@zG@aOUydz^m~>F_0?o#JPKLhPL?Z#hy2zLE!@d{d74^Hn#ewb#VA zo{)mC6;VFlje$?TmqJaJX-!JYE0}2XPAl3EQLddn0MEooM(;%b<&8vsb^a~RS2Q`| zJ32?F(!|wsS~n($pvL(i(XE($5k<)*Xt#}cB*ep6h7HwIzQN11sb@jm!sC`UYq4c| z!uNx^FS86kbD&NSx&7c@sb%5Y?)u1Ec$t=^ziQ`d84iW=ZpClQPihAk&llHCJHX4Z zDv1=ImXi(FJZ%O>U5?_*u^;A|Yo7tm{Ht|8qezU)ZePoutrg+_+JNs&GK=OLk63RJ z{-;3gyYf{!B;t!wj5U1Yh?YX6YAlV|rRai)bd03L`htfdEO8!5E8}#*;{~x{*n=V( z2Q@Fk(vLySxVUqVL`7J73#fgnBK%wiHHP9X1t<9UpoJpsWf7EDCteSJi^fn+UtJS@ zU6;L8*F<9|y|__+fJLmU6s#Z}Ut&YLFM*1I#bdK(W5S@-4)D@Un0>hjv5Y>sf z)(Xt5ff`APWx`Bm1!levYL>WEFInra8mM|fpb*XXVf*3C6us) z5?H`m)+`s_bj1eNYy)e^#To{=R=_>14%X2A7PyzUuOZh3P;#wVF5>4=CNa#=Ako6$ zXU%f0A=fF=+KDB)25ZbU%SD6Ijfdv6^Tlu zwR1((F=m*nmS`1;K0{g(ts>C^C{3-p&8}J^?uFymwCX+$A64|SYKc}Y(W)g{wM46y zXw{m+_rW!hM4auoCW^1c7|6Rekmz;bcGhKlHxv{vP79<@O`Ra(1w8>|SHLG9*kz=@uh=N?he zm_=;B$$3!ULM3{TYohPWZotW-pu9#mU_<3-r=8n=PHUHVgC1V_Iz`%`&8j}~Al-nC zbx_|g+d#iU?eA^CiRz~Py$!5asGYqH*5fKcd+!_GrJ;6aOgo<&^x=!aI_$j$l<)C6 z?9G9ii>|}oEGT>6TZoj;cLm{m9S)xZHIl9~k}iW9N%=|=CG-6gr}sNwBPm~mKoB|kYCK(sVU?iWcw+3)UWf??&+9P! zPEawUh_8db*0H^LP@^hQTU;x#P3#AvTdc!2yPUAiz8*8x9N6Y(G@f$1$h8&Q>#(iT zG{0JhZDGoF(}=ka=c>8l_N;rvTxZ^;=kqR8cWu_ug8sVX(sajg-SgFTbfFS-$8g=_ z={mYt2sY8hYeC(K;CsbrcGH%;i7xclJ+w-`*okzTw&YE;kRz?rqD}Z`bpiiMYsY>Q z=Gj}ZC2zv~3Tf@zZ^AiyyI8W)+PB|?=d+;p?Kk0>T|9Vx1k}FkCOqrC5&WHqAcY1u zVR#ADp4g@>nV1&%CvU>7>Zw)0Cfw>tof&On$;=zzR`n6Lo3`XlTk@tYnKNhCUpv*C z@TofJ{>Y~7c@rB}Y1+Ntgg2Eh8{UMsrC`gvZNb~ONbCG)3*Ih+@)h5Lx936ewgqqO z_?x#a^TrpkD2MMCQRbF;+cIxk=8dmIz#Csw@L$E-mU-JUZ$#cjM_cA?%e-y5S0u(E z>}|o@(_n;kF;Tc+cnjY2F7dX7WtKo$<`(ST1!~ut+r565`hhL?1Kfv$y)Cnczo#Wq zKY&-7Wl0*v-WE2g8j8Iw*i+rKXUj^$rKum-GKX7msI+!#x7-f|{QHl=!@ck-|Lo%Ab%tAaQF_L)~X{}sW@u6i})y)pRj!At3v z1+T@=|E2WHd2jP?)35lQ{HuN^{mS6?f_MG)nO7;l=f<1%?~d<2`qq)#@7TS2_pSTh zbf9JA^xbbAcxUa%+wUB>D|H0MNtrN)*zn#|9_Xs z+6tZwo(i50o(awck8;wPq5f9|uMA!VhrbuRI{5wI-^0#-K>9xn{!{Qr!D}e%PlEp( z{AuuCknnYIzKyT2^L2H;Ma|cx`O54y!5e~WVe!8P|1J14xa1qCd{=NEF@*TS7*g`J z93;J&Zywx&T(=RWoQUJZ^{pT-Y9&!2BgB6rMjF!95#NWXHAogGZUK=3xR1^qY;L=@ za|e)na@>A8&aMczZ_ra8-ku zru=pAJZFyI=gd11d?dIpcr5rra3S~~!KZ^y244-nh5y+nXtf^>J{SBt_)YMs;ETb> zIBEKM@GrsN1>X+-XYjAV{|del`~$mLIjnII@9O*UDbAyhIreeRvV)>k_I*z}Nhj%~JL!Gz>3#MM7?^!yfEk8;kzH^BTmThW zL}XD!1Vofg7FiWpy?~&I%DpZZ^j^KMUXf1T?>Uw9^z;lfqwnYQ`|CH;_0&lvsd~u}f+5OZrn2vzgH&+T8kji#P$=;+IHcs zFMc2hI*lNl)wW~n#x1{aT=FY?_F){w9k?L>QiJz||G^R2apI|G-uBnjhq&J-1VOxf z*Ver!>Gu5XtAcP5?tk1JC+yz5v2jagsUXNb_{_5>Zai}j{j+c`+75g_aMH#Tw>Eum z_Etf7=UrU8eb4SwPQCMc*R2qQUtf;be-s$*IDTlqX7Bojp8q2_#oytI@bFb1wzKmi z*N>Vi@0R~2{!Dy^y`KAr?}*P-I)t?vedXQC+v3moo#x&~<2U@uZ-lc2iC?B1Dj0>i zPmvqRX9X=@K9Bzb@y(sLvCmLJ5^<)2cmWV}p_PubmMk2%AjpHF458!nU=kMZR_x&mk)+dG=>&v(BJ&Y%*$Gt!= zGL7RqXrGJY92{(HevC8dc(6mX;W`_`d)0&WkGI*_;~D(*B%bkUw1GdP=RuD>xIXXT zJjrN+1NUeppev^lUW);=e~E+95(llTw&y<+;~?CE>!2Z{W%X#SwF%=tHco5|a8Qb8 zurs448~Z=ue0%l4_|gZ_J{ITmabGrXnQRdR3+66b#DznZ1ZZGV3mUB?^G~Qi(di9! zf>3X4Xf&CdELNM{;dHq@UY|b@422`nSUl03Or={g*<5SB(AHiob#!)h_w@Gl4-5_s zkBlxF8=sh*nx0v_Wa+ZyD^{*ry=LuEM<289*yGl3*tlu)maW^i@7Q_#Ezxbdf6vF`KeD|e#Mo-XFmJ6&kI*w zea#oX_@!&VeBJdo-1wE7zIyZ5Zn^cg+rR#eJHC16x9A$`9`WrtJgf|5ZG2*F3As}c3 zo1h5wf>$^X{B@^rkMJ7g(uYEYtRNf6ndI~IY5MbkB@hb40=Ym};LczuUK=Xagd)jgB3EIoGH)`+Jz9!kEKIu~FKjcRFIQdTbO=U>AQF&PT z4_!bvpgU9dtUj&Zp}$}Mfgxu&!En9dojOgOuWoVOrFFN}y;(2S57nPp|B5kWJkxli z@#hW3hLsIBH@wlPG-evtHJ;aaQ{#P&ZL1=5mv#X=T#|O?Ni^ z*%GmAu-s^Q*V<^^V7<}$Q=4L&wB2NT+g7$Owr{ZCWq;QpIgWFD+3|$q*UqAIt@CXc zbtPR_yY6?r>9)C-yLY(Hb${0Vw8!G<@!aPXygu(9?<>Bd?;^k6f0F<501X@+xFhgH za45Jscy92{(4x?e&>f-w2>ow(Abe-|50Q?@lhI8vCAKzpUtAyG5q}{ONSv8?p?SLb zmgWzV%agYx-$+TRuGE&)>8Y2}iS&8tH(CZ-KA#aXM`!NM)@3(jAI#-*UvKrb-j;Xd zuP%fNUuvUm$G5%GUTojies%j3?eDdhi|dN_mGq@erKdX_9YY;=b<)n2op*J9*tN9l z>aGvE2f8oq{%KER&q+OB2AAYXFOkU>0dtzP$HTJKULdmWNk#W08MP~lU16W03Gzw$ z8^MFmw55n_G171-L)+t_SU4nEEw)0wkCIO+KBX~`_4~7dMn$ZbB;8TV;{z=Y`lh0I z3sIXjmiH+{qg}Sql^X;HvHK1RPtjL}20>_Q!>x?eYVpxRzW7wy+u*X+)mdE)-ZcG` zH(h6OH8i*^b!mK$0_XoF_4F0YK_;EpH-QBIVmxe-!l77uTd|P0k^25@roYjbaAfPPJrty;k`baL9#3m`cD13#a z)$7gPpx-V^-I8ecg*{F5ZBZk}W@6WhGId5$F^@(RHJ(^1;>7J)9{`Vq?EoGsnZQFP zd*N8ct>BORNKwi~&O;dFG5Rp1pUOi)g}Ubzlfo|hDaX!VI=dptwHL&Rp~Tnr>ira}ok-QLy*rn1?|y^2#& zoEnXHIxslkAY+O$hAX!#%IyYcD&QY?)$pY^xMZWHuxwLKu_@&(ibAeOW0xl%v06=Y zeI}0shbYJ3Fi!?9TWl<(t$vqA4t$|%8jI;LJIL?U3t>@xZ4hAF=gWU1&(O#Dz1oE_ zVTG`sb4P7L7o*jAg~?rPVq!Ff^V;N@8fN7` z*IH-lzNqL<)4Q^f2CKzfr$`!e#+vPmG;ERWQa;z(D%oT@IMhFysCB0N=UQv7Th;!C z%U_@?nZzQq&-l7?URiI@=)#g-AMToZNRnFm`!bT$KQ%+ItPVqUcrYAHrhtz((I>!D zkVZ8wwpS(M2E{AOUcD5Nq{tS!T2W^2P*z4{Il?RRgJ>tk6dk<<{C?_1Qbg-|1~aN$3X+@XQkK(Lib^b~nse&ea&=ZqiQ* z{9NiICC=gO9A+OugZy3D)Tp==;}u2*k5`nfqE@q2Q8pNfMszBTmp9<-QZ$lgm!fRL zd)q#v(TZ2#AaA+ln{@Kc?2z3`bRw#_-gic>sfLq{iCo9bD%Aw zsm(JV@soV9mta1|Sx;n(&8o_Fjg>P{jJ?C8wwKs_#4cMfJKG`rm=?J+H?eXeXNk7? zecfKULD7n$AsS)!K4STD!| zTPD8zuKJW4C#6YhkDuj<@b{5bSl}>(mdCogZ*P;{XH{_zb|X_qg&7U*ct0OiNc%b zoqlZR?9=_@^TW-- zzV>G0;xB=A%(iG0j^%T5-;6wVzX`}_%=GVHg(vBUOpce@!nt;g1MerBTWzg*#+F3Dvktd1dQoMe@OXrqJlh)vF_8N_zq}!lv(ot$jT1GxIIeOWkJ!7X-TSsYZ{nl-n z%#Q6FV-&vJ{O9?Zq{z@LF%mCCDdvIIA~D6x2EWuNKH0!-t7%f)I+s7-QQT7Xapc_0 zwyo>g{j?f;);4(A=;UWc@MH~t-p1`as{JBok*n!Hgibt^Jw)zAOcWwPYzCMnt)6rZ56+e0+qr$TrXpmJ1}ITms7WI3!?6W+K&2w#gR|$O3YKTx*cyA@VO!6kM-0 zI9-1%Tm!EK_fh9*)tVEpQNjv=2GGkovqY_qrX7-2bOfV*D^axCTst!{+4o5+?BcZt zvx__D?BdD?@QAv&-BZ;RC+jkbE<NP9&)VCD~rx(OC}w%=zMB3 z6|&F=6|HV`%h~6h&g>pEx1RkmG`XI^9PY#%whKU#K;pvOfk zv)+~Ln#yFRI+Lz?Pw(dO(an9{G_mDZ^zhP*IaAWMnAIH)Oj)zo?AJRHs+WHmf-M$*Nz0UOa^R71aC5Rwh_t;XZ9UW(MTa zzPm#~Ky_%q2f+e)2;UBkgqoK&I|j!Vl|s(eiCkndEU{VsaSVu*t4` zUeWEQBJoYGSbNm!cwte>T(47TUAN4RPxm(WMNI0aYyKOcS}#ukx#na`H^IBQ2>aEB6YA{rMpjSj=gTYW|Fo*MjP)FQ`t~bSdnqyrF zE3Qg3qUa3hKjT?u!wA#~{7r~Z)F>nqj2SaW+6x8}>?72vJ5ljUl~sn>bCd@ELYTZdzoWZ0#_0QiOindNo4f_-}GrABp~V?Z37YHK9Qlu zWL=iTAWz{wsg7ZFa`cj-iaz=87uvVRrR?V`#P3FB(<;X*?CslnHx@K zhErCJ6kT6hvE;2+v*C`t>$-p!GoA>sHh5QVJGHlM`|`pW$2X7WJ^87%F$0leZAYu) zhQa$D=Dio^_P7Lr%ZY$-Dln752G5dwPkHb`Nd!q}re|g}u2@Sp9kfc+Bhjz86-q_v zR7JN!qaWIE>KS|17AY(pMrW?e$zMR{FsL%g>B4n48~J_8Ta^kTcZgMGT>!&2Ef5mq zFI@xHh`TY+F_CPZECmWhcP3;B_bkt4m-mE+$mrroUyI9`>Iw$Cl1`gDHyn=-<=pP< zU_3gU_de0TwW%(eZgpe_5++k(AltddS{G~W@D`?99gfy%^4Y0zQ=}A+C!}NFdHJv)A5d&*_j!D zG=M4bbmws+u|U_dT+6baa59kJeQaO7;(;rYo?O3UM~kt+Xrx-RZ1?*LgW=dv-V^G< z!cA2k3n!Dks?X3=)d01#stKSB@JG71LMXq~t|;wHFR(uUSh${l*9}Xc`d$0{NP~F2 zF01IWWTC#Q-t0#U)fUq=50g`9R!A_Oh-bt&lcX_bX&W_+|9o05pH9)ZM;0ye$t^uY zrHBD+B_{jE2Xm4}BjpCi`jR4*CGEdy#im3&YSOquv7nubCQ-D*_4jIIy{z%ZlM#ok zD6AjEM7D9?AAS=!J;-D<{3d>ewl2RZ%OoaO&*VXe0_}Y*hZu)_ILG+6Gum(ngtc0j zU%)~xoQpLja*9UI4vzPwfGss>`adxwXL<%p5yYY?P4$isW@U0RLkvL)yU38$Bqw4T zC&!&CI@u9P#=ROYBMG?L5V>&phPPegD*Rd3h1+h_8ocyqKt z1F5c|p60w0>}+irSsK<%|GQZ`{B6%;jtSg|A>vghNUvH>4)qCti7g8jST$M@M#!$C_t$k7)_jXnKwt4Eg(x>UQ@Og7%!FdC5S0dbDkw zpDNZMF~&yP^MzpBK){g|$#|+K(wJIu((u@xB}tjq*XwC?WJ6EKhS7MPI~{asWn;8I zH@rHPUab&A%hI0kU@q3`(d$+IuFy4*e?cL_xG|%Nix@&iQYQMKM1oBi;65UTo9G&o z%kQyjPFG@bU8bWe=a;ERaps&v*#u2pr~mSw&X&a1iB&T_da_)r4PEf%n=e@jovIzZ zY_$9*s0fjCktJ+Ss-a<$IU(#JHJtLU0qd#|@67uD&*Ed|%M6{0e` zSdlC(qvZ05#iW_gvGUsH&TxZBNefxQc^&rKPVgS$?NLTc&g(T-is>yI)C2dMxl^V} z@Dkm@><5vSws%kNo`{6Tb}ud+SD=(i+We08yKin@dH!lgcWGbMUVsYZDUne_`{rJi_y1o=TG{63`)sttf&Wc*PM?*h{ zXEB>oW)T#!lk=fvwJ`BUn_^9$)A$-qZsoV1 z3-ksQx%>ijud?)prCLpWy$+-=Kux+8kvKIh;#j`De7k~WDkfJ1vUN`X23bt9WGdx- zgqbav)F6Kex;SqqTjlawG9nw-iY~7m&;9i!euv%Rpf|Y?=UI%{-!v7es1@t9k`BHa zTOLvEBD$T+5j6q{?as~DnyURMVs2-#^_yGP<)%&oEywrFbgXNquyv&T_Tsm{*1Yn9 z)%Nb5PFsq!vQXAP8Z4Zer!Gs)QS<(9sMKr#HF-bjD)bY6jv5IKR^y>1QxJWotKjpN z^N0lDOkTj5-iu*+g=DIoJGT5aC?rOw3c?^yAf`TNIaV!tNtwpb&M3;0%#Obr-#Y=A zso%Mpv?7KG8;3vp;1NR7`cXl?tsfb$tBrAm+zOn+Y`#E(Fm;A-k~r5xqRMD;Yod2z zC@*W%JwqK)C9M5G=T`7X?2@J)5?Lm#6RQ_@>EzPnibXlxp!_)Ls`h`R{5_HIGzFDs zPQ~*O#l!prlVBQxPawBU_8>V0yy5TlU!{1U(eeuC2MxW7-=~T%1Q5aZu};FrL&qF$ z#&|K>ZZk6nyJ#+n_JT-se!2W;`9&F?05c{>ubGmp^5W|ji+BkFX=cx&muflpeTihh zbY1!7Yc-ndNa4EK>sTxuD-?7T&p{NJKc^{%IKuu*rj zvFyxci8Kk0?ie52IT{KKA3rsD{IH+wSbEVh`TQ{#EnWQa%^h8vKR!Kp#xcd>F=q^p zp0YBNS$T^3Ts-Yu$fKrx@`%Z5Ojw8r{$vR{ttybnxt{i!*3K?Q;7}sFxWk_!YnPeg zW=peWdJWaJjkQ?R&XIQ6*D;e@x*qzWpOBGiA4hl}(+h1)yJS`6ALp@}nGD^3pkqDP z@yO)fC6umO#g@5r`&Pfm%*j)cLk-5{g`0Xb8p2n?;`>&|w3@z67mhC+Ax4)9`7y>P zuKGDFddiGDgyWd0TpcPH1X#;aRVuKrsW*Jl=r`(Ifta;tz|%IB9T}Bj-JWbq#Unn4 zA~XMTC$S|u!n<5~*TiaBL;c+WTW?QWoo#b}eo?A6n0>LHiLpKt)3($QLR1d{5`qF!%L#^ z(Ka7{$D8f(j}FKUGW>fvz9!i`)fKv}cPbxg8E6Y;{YI@)CqZ}xlsa8gb6>J&G9S$h zw8LX=aES^fa!fWFnv&HuM-H^%=v(P6 zdFhQF1yjCGr=2}fQRr1zDP%J91KNjsVF3~Mf`P~)cyL!TG!6?)y<)Tb;F2Oz3fH?2 z15b1sPhqmqF_m*_^mr{Qc+4?Fy+`+jG>S-Xv0-8F_@5r1UD_S;cP=mN-o1N|qIh<` zyt8lpP$)LCzVrB>s$-4`doul@fH)#wlO-ZnW0~db)7YCZb^2s_vh0(~AH2hZ9gR+( z+oJiH;*zz#WTB9Y+UdiRcJ@MgJ|n0o=0=xKb?D1CX|;7@ThBh{v|~D0{~*s!!84dN zC_-b(5|x=1$4|^DW8rQ;VUL4gQ%~YvqSbii;85UAgcph@mM)bwbyy2-)V9x{yTv`l zw{eP2)9;J2bba~9*VCO^?RDj!Tt`ntFj2CUtbZ=wBx!FXT{q5tN-Nz(R-?OotSfW? zxbQecRTnUn3}j!wYu2T^su$>hKiAn7mo$-FvBihTf&7E}V8*<@+>D zbEtabC%}zcQw%A1r}N0cB^j1jeZI9=9SYL{jHmxgxEDD>m>=-s zVp81HPpZ%18YW-7M=?j6resVIji=<5mEBaMvw?L*dZ$LAdOf16vt~uB(u^FZ@+kDT zge7N{XM7S9RIvWYah6aj(-L)IVHxAEpZ7|V4GQU4clvo}ZyAN1r_s)TQN{Ca;EAxY z-NX|S5Jng6COB?zFz%K;C67X_E|)YZQ)#zC{|K~fT9A^=uGAp1`aiFFPWsxKBehh5M+^ednEugXX9!nv8oi^e#!c=_Y0f5@b=jXx}xwrp8oHkw~UcdtzWoM}D31VZEz9*qig1y>V+aoF$i??-Tj~Y!vT@kA{7iwaHgz@(WA@Cz7Cazn12e(mvZC8wSkY~6P9_>-NG&j$ zjFFxtspLePPhYQ>{c>Ghuy1u+>DZB&R%g^z$Kg4QgALkTwcbojKh_dhxD!8@V56q7 zJSV#pf1m%nQ*Es?rQ&J_RqP?s6z@&K6E$tv=^j()?_ksaPSl+HvBH}EaA)oqh1~UQ zbi5-RD2+F7+pVoW_d)c#YD3JAvGvi~6t4~jxF&8SDZ+xOh^#$mN{qI(EiHPma?oc^ zB;wWBhdWttXNQuO)px88E&B8#FLAc?$3lgm>BE-g1F=xg%6!*)wclWrIz7fFP?}0> z#VGy;T3nuNH=_rLvWdQf7%tPH+*CM32FE6?IdAFjwuya;t`}(&UsrTr*W2U1rj`c! z^`H#Fy=+s4F4viT);bF3p)9}4?)lA;gCWA&}$S$l$5;+>I5)?;wz z$I{kh9eG`mwW;yr3e8LV<6X(qLp>R{M&D2;Dsg?iB8IvalixiWnCxqgl!koCW&JTO zJ24&)fKQ;`tFyd1Uzk~3UE#4pvycbaDno8HW{85nK(5p2Z^t?T+=|w8f1)Yg%;y9G zZlvJNhRlt@T(JCNbxvYbcG8RXk6${gPRmXuAu9>oN9Up|V5G+7*@tWCJe;3=#KXTE z=9J^%F`g%a)qXXCzz4D#R3IMQK#mizGSOIC5)E;KBx@R)&Fwv2xpw~-5fN8Tdg~F^n|IRYLSZtHT!LunA2Cg}S?Feob$(7>Brx(31>cSl-b%H;1 zPK8Jj5tfxWe9_ROl9FAQCC@=dKr;W&hf-A@*f7q_XPYk%ip+d7Rat{zZIJ34o#y7W zMMEHqWN%Nqxw*ljz`!&pnc*q#`TsZ{b5L$RdNQZet9(r#1Fx}Ik4Y7Ge4ZomO7+yL z@{Q53N>CeX)5n%BEi7|1t;}Yoogz3G6mTWmTs>WAxRY(pz79^?nWNg$>9%xi6i`f+y!6^uQ z{0`|6)0^5`Rt+a4{jWp)Igcwl#OHvWFd91;l@Y6jQj5v^NplRU7`E-Nd~M`0kt&U{ zug8D-Ii}=zN9P*g5`fjGq0UryB;6UTXV`eVe?{*@&LE;gmz>(VdN5ilU?q&+*nN6; zs4wdYcZ|n!i=i*rI1phSz!2&nSVko5ws43k*xEo;dCg9S5VTixEi-O(9&BWbwz_o= zJ-Oef&_r~wO52nt8?q3MdS9k5S7Cg+*!-yF_;7(aUsF~+J2*b`+`3iK(qJiS_D0N2 ziLhHjpw2hwcNG#&e%%^MTAY5Tt4Y?n7kRy%8PDEid)Vbo1g##EtmqVpdS#tra3y`I z_K4FH4>*DromN*TD?tfMqF#(S`rQfqPNo}pCZKAkR+U&a_YboeLV`=Dx!JsjSv(qx zC*0)lo8>zc-8Xa=IDB+e(Ty4~cbvU857StNdV?ji!JrZ0=kmn@O8}J-h zCv&-?JaS)iX;kl$aCfgyOK*^o4`D>Kl0T7- zIPhLu=`;FI}D_(O+L7e}R;Ayt3%Y2Kwf38F>8PL#1i@m8132L&&pKdP} z5)L*{+8saHL2lqQq5~UF-a4t%2GhBu7cQ2%8hr9vxWTIJbT9Z1D{sih2y!nFt8Z`s zgUEF)KS2hS^3Bi(nernF*`vU8zt*DQRNmGE1Ay`GAB4y02h692uLq->#m1R#MQEw1 ztq-gGkfCw-5(q26o4`b3^9V6?Ft!B6-c24?>`fAaGSu2o@1#1TtZ0$;>v9?tnL6qf z#bVY{WDbSw?gocak3@vx-eQA=idex-%ZPnA6nDaAGk2WQVXRXc6qDQE(0OvF9g0fD zZ$elP-3;FfJ|0(_>k-z}dA==vMNvN7$ma$6@D%z`;H#*J5{emEt_)2?;H!ArjTAOX z4SyJR1mT$&%L8~yXTbC$ekbC^1`pLB5KpZ}hBZ7=cO7DkZkLX=T?VqXK`|If#_40J zh(=GhH?yI|*rbsHiF9LbQ?7w34mpx9nsXbo^~}D)_|jG6=ctil783YY6%FbdJ(Hl! zq~wycM1Ec;iAI~>>T1pBT}mwO*Gdx6n4Lkhv(TD%dAlw2b4?nf+1OxeFf>|qRHu_0 z%ni8UspU;ou0u`9$C%$fm+!<^+O)T_1!RMF46d9kz>DVk_e;Vos#AE*{#>t_L|BlsKdZwK=nO`K ztTQR3uF-7NZ`yasj7tr;bTS47nKu{|g^dYsEWCo}s=1VB@Vu`E9)o9U7VAIp1!sDO z!WQsn6_x_6-zlCq{`SAZ5 zZ$aKVVy>S>teNHHvW(nS!ba9(#)H*>2hX8EESB+stY+obj54Mnc%-2?KSOtZyE<;F z?(G26g%IBDw+8VLrXG5@Q<2x~+Gbgxd!iZ$^Q*h1);`P^y5Z10}I_A|<*h$}(G%!nFQoeAN z;*``dnO370?}9H>UHkt){26G}Da=4KL8U-(s7t;Oc!0m?y==*z7Vza?Bz#)F3Hx1 z%MS%d)U_!8$j+&$okNf@9t0_|VoWGur77x)@x{#@K_gqBnz?jy$MR*@z&$0q7B0bX z-VF;^iA0Uxu@G<7_RwCXGb5{wiB~33WPP1ZqJQU-q>zRofpmqIe2UfCF?|pgZbcFo zWHhf5m@^IzDx+yx-cLwp+gf!KSNOQ0hWABs z2j)D+ZQo3PF%t~7w1k2!{`fihmZsI%;Mn-3DtN3TVH8nUOH1V%++AhtbhwBb7FZ7 z^eMy}qZ6^~kq-f1<4V~eU!l0lrzn4t5k7acI$(^)M7y(zY($o>X8beb#IHfd1PsK6 z^82uJ9p(2FGKMe`TN6Ms=+k(oi?SsSE+g(0{s9@mc(*-Efta5sk0T=wc}f2e@0Q@O zX>R-q)x%hdrjcw#$qs||TOx5t%GKBC99l<&U*-7|T!)I&@GA>ghh5F21dABJ&)!cI zF#o-{e{d4EDRRPLa8w0@c46AHIBiOdv<>WZ8Po0EzFLT3XqU^jdpNv2pr&oPnhWmS zP|8Bq+_5@HX1=`Gog0WouwYpJ8nNvche%sKOrtQZqt~7CnQGC9-u|I3i>VgX^p~d7 zlxm_%v_IqWwDe4{Kt-@~I>iGOrD=>G`invQi5TRuHuzYai1Ta)ZLKcvAnQga$tgui z-O3?B&iWx1k=|=jl-ghX2o{vs+VTg&Cdg`D_f*S6za|Y z7Qu@FYBmum(K8;44!4E07k~57*@QD_i@9x*4qPc>1Ar>8M8A4@|3!U%h#;AbenUPIjU;yCg&CId$x0k z#nY!`x7@rmc^!A2ZNYfNquDFV=bw)QymF4BoP*;8(GyFC?er0?cI~&L9z4#Uj6`s z`e0YpbX|Obl@T$o;|chyc8h4%>aA`#t2!e*xW;)G^r&W*bUIOIZ8X#?Xt4&_>h0(# zd0p*zuTG@)VLx7Xc+75xqu$=_bhevhZJkKe>zy8p+ir*Rt!TrUq~hsx(S~pfGNhP% z!s4Bio~p{Wg%X!I<+P%tZ&kqFY1+@)RoRK}sy^QPm~%~tgslPJE{td|uutV`zF0N< zS5;2XUi(QD=6=xk+m1bdYU+aZ#p3!4rl!t6wk@4qe$v3eo|U=W$~^-EyO(9Z!j`*j zp%zD^7&6rdi_PX}z@gQMl4ID_KGD)-&yKbF#yw=i#KoIBJ2zfDIeF2hj?Rr2P4=I> zrchYDx4(Zc&TIDmx7euY>gpTnJ=xx1q%ZAcqDBlU2A#LKq_t&nXRyJ@`%117evdke zC{{)LnSSR+7S@zOZcawH_FfjhsCrnKo`@HzKJR`B$A$cFTN37$Mr729wBG76q9VP} z;!A|>QKPOwuluT^`>F>T!ecet8XR5(h;-z&+tNOR0S=^ACo6iT!DFMwpf8xRHhGnT zSwZ3Kcg;a}qsP=}F)&kv(N(wy`w=jIr3i^Q&tpM03iDXvVRnt15^!Ts@Wv9~i{z7< z>*gF8EbPON(K0{gGepF7X*}_C6rmycU|x>iK$UB*#)^$aWQfkxe0rH%-NtAGIANKWKl?1agod}8OfB>EKG#`M3_%ouJYP4;h4j7n}`X9 zFda(*Q%g7@>6sze4mHLK`twDRB{(CXh#c^8=cbD%ka5=00S=kGcw=W8tN|8T1y(tk zv&t7JFzWj%=E|Xk$mmri&&f zFWTIRdr$VCvZm0+ZppZB&B^6oAx>$|+jZD(fokpkh}#E(&-*|#J^02 zA*aRSbXl65bPR4nFw~L>HhJ6@iyNskjEC+eyJ;Nr8}%1VHtWk&O> zjLNgD{^f@I5&8`N!O= z{mZ=lHMHuM8wc56L*=E)6kn6U>JVSZo|DsvZDU-S+f+?H;#!I?^S5Jh#;qW+KNfEF z8<}y2Fah&|B>3`DAl}?iT2gS5R}gF{M}}Ixq4ojq;03TaFaOjr_afIqqwUz!VX_;g zK+noH##@!^$vK$MZnh>_%`@QsTF%yv8sk>gX9E=FqDoyhQ48c81tE^jyH47wColn> zihCs}`eJf1L={qpNpp+lBZ=8D;(S!&zhpJ%yOYx}_@?5iWNwKt0l9Y#!At zd9@0Oe1b2eAOh%ij4p~y#hPcTTay6b(Z)-$Nz1-NK2kx5sXd@E2B$nZhl{W-(lc3dfXL?U5(@IfvK(92oDGYTUiL~9>U1Eo zGFX1#fT$Ym2XYm0fd}f*GnOB^FK*FC`MNALf?8=&s~AA0jjO-dicz(w$8W&43rbBLUP`Fy-rsQJLEU=<9j?JzkLZY| z67c&e{Ybx`?Wxd2Cpo=?@ch>Zn?Hi)aESMXbpLt!&yVTS$%WJ{Z>WjLAN^BYQC?Y- zhzIaTw7SR2zyA|_LNBaJLB7|7n@-Se;Fh4UT-YI;dIaOrwBLd^=5asP-_>D51)b`5 zv*}W@A1vIz-w;h4Fhr-AEkDwr#4_4p=*!0KK<6( zxBj|t_~!QSnWud(sg2w9R~=bdJDK~&=zzdBF_bKUg;xfXIu7LagU^O=sA(a1QD z7_B}+mZ`;*t~f$2;Q9h}h2kTysyc-pM&sI!H#2B z9Uf!xF1GO)3+q!w#KI<4AwE3n2+(D|FT?dR>@6C&X z%!ZBt$r`V;Bmaf-HOmo;i>x%MDn@yd&cc%4dG1=%zWU!i7E99SO*UETGR3ZpZ$T08 ze-LYv*=9GJ>@)whuN-(#iCKKj%|1(17dr0?H(anBk%xU{!Sp}P4yVcFbe8+n`e0_8 z&(e3{mlcF=9!*sd4Z{yW%>*{#;fs@Pv34YR1?AdK4NV0UE87X%UaMfS(ceHrA>moP z#!z2hr$MUPtCUzYa`_fn>(6v_wkMq|ckQ|xMEQmr%MV@Q@AqQ|0lp)m28GJ_2iqRs z_bFQaqAhH9mrmH0T5?SJ>u@|ifp->ty6Ve}@_J$9_942?V%I$GyU$>pAMAyLfs!jV zM7N(2KWD?UaxrG_7fUwS1hul_j;%elvf@ClCF8Aye(%0Hxah^SV z!`;#;<$o+FEv9cBSbQ;?uhknf_i#bA@jiUNtKz#*x*t&Y3~cAGfLF_&C*+7EIw0rw z?JQZGkFGUk)g4d>E#4(B*l5!G;ZcV|Rvp`y#Y++UK(z0G$`^58>FYs8mjyI~GSaDd zhSkCw-biM< zSd}g>=B^H|M&nkLyT19&yI3tZlQF0v7ehRbir97QD2lU|uZfzS2GOHLiddq96VpCqkw zcV#hGgK!k~&6pSaIEp7C5ewVQwl;QjTAW6$3A4cFSq40^jj+%>(}3Y{u)1n*J+@!9 z2dvIK^752e!e`J()Z{{<9!uTx^jdNUE4&s9HAA4zY}IM&HKJ+@xGf+4l6T^YgV$s; z-bYO~cJXIX1k~5=Z|@@CZsEGb!sT#spULU38N4CCLzI#3ra64&HvM&t7iG#y(NKX_ zzVfie+%CDRgwWmYuaUjvWM$!j{__Cg)jwP5RxWd|9K* ztP6}BKb2fdcKDYr9kqxiiwQ;93A?j(-G$47<70zPjmzs4Ww95Z)pu_G_zX*C3xvss zw%+04KbH;m&fZ?n=&36*X7CfU1yI8-K886^wFUOsSulwCD&Ji3=s~O6$$1NnEVKL= zQdJLA=>FybHdYq3PDfDsK8N%7YB=vFVU7gjyXTe@6!_CzL0)-SjKyysAY;mZJra~_ zV|-Z+-vgw~kqk36FhRpqZrgWI8JAkc6mIdbSaUg3v$f(&5;u~>k>ITId|!25vYcW* zuKRMFQR|Ggvcqu39rN@zw!J}+3LYTs=8YjMz=K9V-H*CtvC)==S?#Q%uq7> z>wls6oAVO-%l6i<_IJM-`>*$Q)qGH|M|}1_guh_>Yf7qEU%H- zYwErN5>_&~30<{;4pFkthzr+YVaYzfnG&)!ceC9LYU6-H+1fa; z-3&TbBKZ~LFn=HheAvu@eokvbNDcwIfYm_FwUV0I3lB;9gO3NdWyqD)e4}ox;v66~ z{~u=dY97nGg&R5l`L8nh{4A&0mk-&;gKy+e>m$p-C}Ah@qZx}$Wd7MT~x8*dPO*`7eSE!jykVhLM4S_ zYBJ?Ii`d9=u-o4jvrhDl46~gokYh6MPxPgu zS(gs_jOpM}zNZHA2bosR--hrcLk37irbG1+V>E{JL z`=NVu`rJ|#Olvh$XXKymQB4=Kg)8-^bISC{JEYnhZ}NS8y4BxosOjM9-ag_XyA)qQ zq&Mdq-kv+*-}|aPa$5__=WU2WJlLlJ8=^E3?1&OvbUEJ&f9 z%6_j224L;x9QUeS$MOZ|thyRU)k2mB?i%&Hdiw=?M?oFc?hSs$9cy{Yhl+9BC(FPdby(X0$=j25p z(taC`sB`>%E{6{i7v<~IhmL%aV-IwdK7060Ne(J)pF8YMB{h5*Z~jYh(JSZVDpCMc zS7-eZU|VDAeUfzc{~Qik)rWZ>@{{3;)wg}z4j<+t;-YhDmTCx{yfP;=@1s=jNv%#X zx#?_QQQh=qg}Uj>+@3EfbzlK@p{5 zic7M(rvt1E5OHl2+r(UUNm`@qnF{XPcF5Cb>p!R8YFkA9X0fyVhdf<>vbGg64Pw2q z9m_p|<(Vmsu% zHYdyvrm7ls^pIHAWYR)KdnhFF8@3qBm$*$=T?JbZRoNd+HK&<>}6XCTwH)`d+F_mfH3_Ho~k_GbBJJ$0Edg23QV924=lxJfO{SAm zQMo#f9}LO#;0Ht4Pj1Zr!4USN9P9@}@H;)cC}vhGniKIj`{9r%emI0+rqkaJdDZFYq&S?ky5(xhtlj<*lObldYy;6P)e1!Fwf?JKtF-JHFcj%O%y_zuU~Xy<7@WQ zXxM-mzo@~snvY}WBl-!Y{EPD2c!Wl4bQ%39#XxQb3LeO)9{vbk+EP}wr!{iXG<9`~ z_O|i^w<*ePWb8J!zcoAa4d6f@(uDn#p22g6fd;D1*m9>D;$>kj&h}N~%Ff@#uyICO z@Fs48r;55dQ(e8s<7-gt_QpDFXJs@Qj2^G2!4fjC4``isqucAPzAYN+&4zjpURy%s zPCdmh1L9Xb_4)?<@S_30-=(k9yVmQm9hI(5$1k!U^WNNMqouMWC&V{3DRj~cT zkYE6`gP5m57!CzGV{th(@rkh9CH)o2`zQ+;Ebl1pENUf9zOO@n+ym>i+Syl8{1HFv z&&VssKOzXn-6oKRCoz2tnJ>ZH^x@Zhyn?@S6JVfnFJJ_}(Crm6mG1x+E8hhyRR#bD zE9~>bmHPolDlY<#Rz457sPYZKvC3BgCo1;=PF5ZOoT~gg;PmV@fHRfNfQu{d0j{Ym z1w6j;Q@~x7rvY!_&$^XA{Sp3|A8`0+*xU-)-ZtR9THHJ-pq=;UI@YmAi3$n72nb zT*To7hf^F*mruuMW-6BgF0DKRxC|rX6P8zg1h}H|B;ZQ^N!Dwha8%_tfa^F8$5mcO zdn12$6UTouZ*QsGiR;^Vdq?GGXz$|f+c~_S!-qI-k5+z(cc0|XeTqNvX%3&^@L3Mo zSottk&jS9KfA@L*-4{6yFLC%XcH;2~ukiM(9KOc!d7Z;IDnG}&KSSG(5&k`3u<|j$ z9B;RASgM?ZD?Pm3$Khb*Ra_YcmHprrwu9%Q$_s!K{Q7jc3GJE6Re)?{{KAgPF92`n z-@TthhIs(gd=JoHc?~d7`7K}w_yoY8ZvhrT>j3!k6u`mC^MJ#ZmjFljyQ7t-&|Xw| z4sZ;Z1n~4102zMl`-sy7eX(t0-P=v z0B1nc5bs5Z_acN|yoW2S7a{cGhk#o^ix7J82;jELLx9^k4m&DrZ)QfF5PHGxa|?g^ ztsFkbuQ2>Wz@I&l;U5P6Hv#%Fi^IVG9l#Lo5C*+|1Q_GjGyF;sV+)%Kycb3lGhrxlYJqgYXqo=z7r)PfwI8(V9kkKp5dm83_45N?FDOv5O5p+?siDdFnaeI;I7Ko0B_+~-O3@OPMFX7FnY(H!g?3M{Cx;8SUC|eRQVlX z7|)GBHogHE!yJ!*MghQF&?^Gk{Q__b^CAMO*#T!T zZV^cPYXFy4E&*Itxe#zUZ?8ZvB9Myb0e31S9hV;C4v+2;|An0gvZBx`p@X)=CanZs$+ApX2`s z$M6RnJ_67G%Kl-veY;K?a=v7+@G9mf==G2Auy3w3$_q z0ngtD*v4TA)y5fc`*#5Q_?5v*H`+t|lfxBQNa)e&fQu@t04FLQz$x%^2E8}~aHjk| z;8KiU2K@gsz~!J(hR^W~`2VM9Gm2zj75o}-9lvrM$7f^Z7F^lHuW!a|&45NX0B)Lm_ehQ7$aDqvS$$ts_Xgma$`ZhJz(0rC^j*Nsu!3@+)HeaQRkj1N8I%K! z*wgRl@JW8pr}*8T=I|K~pXKm5{@fp7*5^PMhR=)q{x5O(GI%2gim)fX3fKy+eiqPQ zxe_o?xe+jeac)Jg?*?Quu@(JY4>(x47;qRft`$^x1#q--8Q>yd+X^at0dN8{rIpXb zR*c4P(Vm_?4sfQj36RalR!)&t(Bh|P@51bE1x47iZsm~8$5uWcTZKm<6*Ya5_L(*gOQ2mI zbYxfhIGn-=?Fb@hoj_Ztqc~J0Kz~eyEJm`1_;3kgG zW=Q5dsQDz|w#xSbnO&4euU`keoj>t@4j;u_$YaK_XE8fGFEBeiFZ|!G&ICHHtIYF{ ztxxyR(9=CIbA~2~oH&_b28A*b z;x6z8*d^$*5?$$6=yqXcawSGzB^rATYNb<&#jD<>k;~Z?qH!9KRjWF!Cj_$mPvc^RvT2)WF82goI^-*}79##f7VE0ONdkyUK zo%_uJe{0ZXMY&35cJ*q6xLt|n?}1}3N0<>R(fk&S?WACwEU1!MNv%)#Tc_N5g1M{` z50s24bJCwqn=_1yO1v=+(^$3=Z&c6ceV+y5UL{sjO~z6znOSq$%=yz5EL(}yq>VM! zyp^8oE3uuvTQH0GKqac!Q(C`clT^xsDJZ+!-bDu}q zK6LUpH4$XohXyXdoUd6yKl|v<4EsH@R@_Yc`mNP3-_w5ktflv(0QbL*-@a!#jTiwIZ(a z=&SPRtFm-e_=&!&*k0uoag|rZRgA$+YDWF3R>W1vt}CN>Y!w<%Su5fy#-PfR=Cpan z)ZAC)6>*hEVwFc?6(ezldX2;?$Db;X#42>C`>dH-5m#YNQ7hsqtf{hA#8rV-#8rV- z#8rVtVwG3K`*~Ma??s2m#P%cgc_{twrw_&yk>l>@}#r(q{{Jb-RzU>8ySfOUHS-TsKObbA1; z{tONgIS-)KZ^1DvegLgTp)!gCXte`QVEqGVRsETCf2Q5~jN6(u)Bf(9%k$=fx#*r` z+=nGIYc88P_gQi80NR!;YwmO2eJhy7od>92ce!M4xUEfd3#%PK+fVR}=Mwgys;$pz zX8eDmEPt+cE?4cDqS{)mMyr2C{XSL@)o8UD*02Vt#-Byya@FK=KZT7um%}FHt#&R~ z%{=($l-qs(P`QtCxSWHXNKtKnuC_l{Tj$m2{0G!)HmSBhSKFVfJ*QNo`5#iRIi(uS ze;7u|?yAwe?yNbbI!L&!N%wi$J=6|QHBswXxaf8@vsBxktL@L#_UCH*bG7}s8VyIe zMl(w_(P;t7pR4W9)%NFV`*St^to~?bskT2?duF+g8Rjz1UMQ+uM^rfs6;-aYS6}C- za*%7T;2mULbC7Fphq~sVuQ}*zYOv87@8Dmy^S7{qwOb7~IuDhZ)HpM#;ST#LH|%^F zHj?esI5VkX_W1(k7CgCz%;YZE#{5#_%%p~x`6^}gt%i1EP?<@MeZGdwq(nJEQXl%W?==K2Ny;og6}z7hp5_$swe825R?--~47yIbz^@d`7=?ueJXnWKOQmPyt!a5`n&RBwT)_Rqgvaj);6lOjcRS9THC1BHmbFaYHg!hy!|=e z0MgDzog+gXHkzV*1+!-zea^xwSq0P)8CKw4WT_)Eh*kdX{+%O~YtVKb@j+~0H@lAb zunHTQsq2vO0oX#8Qiohg*zQk5<+o5)hFphqFG1y;b=dJNlpX6FCF-!|5@lJljwtao z9B|);@Yp({!~z^+cB>;wsLxSfA9q(SYdqSPHT8CC8@2a`x>+^2CU=}UQy2~YV!@b=!x7_YF<$A2}GQ5mY zP)}a^8r)6hQ;+^1fqNJQ^=MvK?q}z)9&PK>Mn+{ldRDoa45uD#tELqj)uZ!Q;q~M{ z^=SUvFjT$-hVij_Yq;LJtw)oOQxm~n_2_UF#+WH zDrIV2S?`Eik2chnB5DIZ_D!g)y8#~~ayjd6z|(G^tZb?Q8TCfb9)G%*`K|#^6P0y0 zIA%8BX!K&naaHz@GgCNgL$(2 z1CM z2BOv|EV!oV`>*@Tl4~}0KEcT;%RVfU>Of4HA#iy2uVe;n#2<=q z&(6EyUUHj8{NVwpJ~X-yjrfCVYEAh=qkGbb4{TEoT^xjAMpGj`@HyDIb2B`)^AXtP zayL(Q8u5Ws@Z=6L3+w+B_L~E~X3+N^Vh5>_m6uu^Vc)xv@p2y=^Bp40UyY0!^)p7M z+KBa4GtLa&$f$W5o??tNq76xu+Svyuef_lWIfL~ZvAg=2W(C)X-Cu|EzGlJoi|$W` z^;#o#mn>Pvdn0zg8|EzOiod&R4V=XmjaXb)7R;hQT_-Q?nKiY`-NYDF4;Rgh zxnyRUFPgB0KFwLWvt&w5=wH26#@<9Gc@8R9Z$k6x=cc(0o6-3<;HAjYjLts?uRtfw zXnPrI)zgfgvv40P^ky`CFRXF7)@*0YHltyEs-2u>G<*SeVzp*Ge+hQ^KH8CMM#GOm z&8^L7bp{SG`kK+<-Ea&)X=X+^4YlfNMtdr2)zeHI)BP2@n%SdMi!*L()=aZk){GCU z^?7r_Ty#$|?!%IqHJ458_B4A2X(o0_mNl%|VzG^0t?l*|qHbJN^nL^Kn% zO8l1fC5*%teE3zk3mID+XIjwe9h9#m?zN!RHMqy0?xn>RbSo;(v^dVRpy4UXwPrp3 z)8aVOLYz@6q4Jwxn5f=@wm%6wiLotcdkA*9tT@wxwok&7J7jzKa|-sG1HNX^uO9l-}$u*E%aj0oR?EhJHt8My_2z8l6}GfuYO;yBadIMaeZOWu?@X^E!Y{~1;m zEyNl1Q#NYBM$*qb}LR%G+Df|0L9UvlVY&fZ91}MZ;52J14DZ z_%kr-Ud3J3&PglY{yLm=AEw=|_DovQmDM_{mayPrvN6?k(Px{IMmxs{d5p<<` z?S~veS1L!@1v-Kss;qqb2)a^v(wsJD+}5m_#x6(jI<-D;E|}U6If5p|B{OR-n=9z~ z2s+fKYi8aom__$t-Q|+0osc8wP}EMy5p?(j=lCvRT(_aaRk(|MwGDq;hC1ugh9;kZ zinMKLQZ@Tb?S!X+!h(!ZFuJ$XMFY{5>$fGXW=j zx5N%l)SOqe5tG%=Gwz{Q@okKHF^w;^p?{V2e4&j|FD|+#8FR_Zn#-ng`!;)W8&-%? zR&L*hC#%l|vq(&6!;@80GB@1YO>@ibZd1PAarJt3|1PC`61HQPm*Az0vUVc&_h1EC zL_0P*057M9?O19N?%8RDIz`itbzX&v)$QE#1gvpQt*P@!?O5jmY$j)G$2uk0iYD8! z(K)CSAninti!i+NC3v*VN-5|pbD|D zFkzh}EpN&#>YPtImX*{q*32w1t=;o%JJwvIyue;)d$8#KXRO;Lo>;XLk))-ZWju>b z+p*mr!vcG2?O1IYmRO;;6MwFRo6HpLjGB4gfW746*Wjh)kHgD2gA@uXI0Y06c5!Bg zUsvMHNhr92^BAFEHzyfF!Ic-EhF7^}4^MSN!3TLd84C7ZjKiyWIu;79DR;n1o|1%u zeeC3if@|504+T}_Wq5#{+fYzl?uXa0XB7$#Uc4RFxO}MGMY*&2hLBRq)?1#OJY5I%4zY{v>A#_4aMd>|Bb zl-~~J2_Zb;ZBU*N3cAau;qmhSfhVwji1lL(o@CX=+rs{CZ&|JMxpjF+2oHG<4wX|- zz7eu-go4qszAN7d**8M?1}j+mMu^dUCuR9Y2;X=IJhk&jFmdsJz@&AXGADQbl$t4P z^#SW@+O40lHfG$bSu<@(=gN|L-Zcy6BI7y~WGv&7nZ5X1YL;EjSu-nTS$5TTSS!o= zXYu(^khgRNvuLiHC3C~lZJJvb%Ur)rIqXp%X1xD`as?}nFgd_)pvHUH<2@YgagD}% z*yBCy@g8Qpf0`PN_b@S8Y+%%cJ>J8N_q!=;yoVX@Yf$4ojIGbWknb60k0Ok%)ncc4 z%6(QuAge78N zIM{H#MtYc$J{KJI%y`tZ;ZZcq`oS~ZQM7sw+1wsHTX?Dp5gkg2hA)brO- zBHlJ-t+tM`ORsA*gBE#-PL(hm2ngTCEKxrrR61G|48w(NWqwvrWe(9bVH#rY0;`}Z)!xb9%2e+zcA zYVL5KJLt2@5j57p-1;jRqt6{k@eI_Ou>(tO!i4XWbgxpTr0cMB9hR=c(s6c`es(g8 z-3fP-XL6DVU+qMS>tKV~h-EwJe-^f23+8|F|4w?l3x>=v{?keR@HFhChn?gPFGD>? z?j&>Agvx9?$r;YUAx2Oq63xLeU#}-7o%CGyTy%||-gc5}iF$h5iRXy9of)W%s}orw zaE;YLC-a5wTri91q!V9KO$i=Dq6cA!IC+eo+`zA4T*?kx7gFB^wO7z(+jZG$U0Cf7 zYMRK_x{&%qunq6+Lh5(HkSUMq!bZh0typnOzji*JRGKy|X)QzXTNLhPa-DGrk!w~Vm zn{45Ac$AfIHx|jjV`#e@i!8!!_Ds65i|%s+ajBdBv!ji5bmb7f+Km)nhcPVGO>BP# zCeVC0Yd7^FWh!&-CUX<@B(a+a@HABB+)V@!7k$r++g&oV=CYY{KUeV2ZuFr3ukHLD z%==pfv*;_=T`s}nXlWHHD?V;}9mifjr`&{|k7KVS)VY`A*o&v9o;Qyp<1Z;ET%!?j z-1H#+dPtZ9&*%exX+ZNJ=|T2~U<<3x9%TO-Y%gy@Mb{o=&qGCk9{f#L9%Gj2!QZA~H`#R$ z{&p|aNa{Q{1%cJ3h>oP!a68g=V&m$j?YgZ9+or29PWexC6)dZOKf zABt&a>K^-H4}Pc`J<;yL4n#-mv(qoJC*djf)NDqFf9u~|Z8G8>=MKvXJ z!@b=!xA3kW=BaZ*FIM;gl$Z8mg${THn(xI5KY?1u^?DuGixs{~c^~oDio?C= ze*q4#ChA51FThbO+l&4mgAsNbdx>3>FupSa6`y;F0Q&Bv`#kL)o^jvO%o@FDSnX!q z;*yy)mrZH7*Bb7%hI_5yUNo#e6wKmIf%3Y`C3A!5*Gsl9ZsF&>X!tdL=SRBjvsd?d zoc4J{^r5A%QLk~zPSwtBP~)`E`EwsDsl${NxBKjeeMtH$00EdTFE+bSzE^gA;2 z}0cHo) zYb+1YPu)SIc!2rhaoECB<^l4MRoJ$3J5(MrU|kKMtLG?7R|E8X1FGi(jzR7K3hZ4l|c043cZcKI_HDo;U4 zH;8l>prji_I+Znd4$X$ z$jydX;ZDOVnRkZK*ey^R8@9%V-TJUwA9m}*=&FxT)rVnprTeHS!`9U>{n6b*rgSyz zJ)&VEOrG*FEHzAoKMRlhThie$QAM8)5n+ZMH-_yG!$_pNCEcr(shyu;@B9oCx6e`5 z3SyYJt$OYJ3=>s!w~Sj^GPCBgDg6vvKf~6~@EiKc`&$LG=quM4sd`3kc7QTlT~yb_-urB|FGaIZ$`)g)!DaoD}a)}!<% z1=~62FiKCNFkHR>JDF`qvCfOI3y&SeI!{7H{!y&+OE^Rn9mU)4gK>6OMs2B4B76ho zMc*NV6r*I|VwPw#N(Qd7BpS6uqn2p&4T!5DJ!{Kj&P zA=l%SmAQ=}*AHMDBW?^0UjlU=V9X<8%o-j;tEy2S#@vT7_hHO^h zRF9zBIkaQ2}*BK>y4-V#NMd&7Pa1@=- zahQdpwrLc-y-c|izlx%_bFjR}dW)hreX4!ts5KV#%o0Uo>QBm?@qK1ZotB9b zNnV2Uu30b_eII#s)LtD$V=Bw5qt;l|8jD(EQEMzpexgqcW|46bMPsTd!I-@|hL%oK zzJf6lvxmp>`(r;Y&Y-@}(F$c?b>>4PyAxQ!wE>XhjzDiY(?#IEEfnqZL`qE3z1)TX)X*o=axd zTsGx7G3y~_J;bbs7x5;qINcL5;Y$JtuC@ ziCZUedrsV*6UTGNgzPzSJcqqSdrsV*6SwEEJG=8v>ZPSPT6zI?GQQ$yX$@*7ile0r zl$OYDTr)&`i}SqlX*hu=#O+0Kdr{ouE{+#XP(R~)&YEdPM;z@v59eL8U@rQenvddm zk*N77ju(m2UfkM?TYGV9FOK%~X~8U##l_K{YD#e2UNnxzK2KSz+Hw5nB`E*ltPP`V z99=yEo9NXzTDljuF%rk|pZCI$Db0+dnP0A>IW#edO8cH+cXeI-Ph!x}L zWE&=Y2hGUio{`7#pYxRUEM^=Hh?psVxnFk^E9jXe*We1{fn$CD_R+ldbo_|iMzQMb}*>V2*WbR{a|oj_wMpY)Xj zE)SVniBGUr(x(xZql`g3$YnieoM0wZD|(}3f}K+FjC(cXZ_S!%*Uz~;Z!VaNX2x7H zv*xn7;ybUJYi8c{1yiSJCeW?=Rx)*>Z-OWdnZ5xg=9Qd5ZC?D_dM{^*!5MzMdK66j_SC72@f@DORbE z!K1FxJKCqvu=gNS37+yD6252BpQg+i-(l8N%s+*%s+D%|Si2N%(n9!cXoJeqNKn(`KmGle+|ZI0pOtEj^b> z@LWcB(387_pWG$5@kKI7xo+Mpn?T}#9 zOU4!7f7P<*cdn;eFBC#cb6n)J_QvAlEi@xsGSpb6Y=DvBWKb(S4rZhYV^hvSv;A1l4$Te)O(#t zR*v6+I**h@^SY1T>rA3;^-b?}CW!{>n|6bfL{zn_TrTNcE{SecRxX!xL`^!PCLK|e zj;Ki@s`{Y!I+JL)1oiYJiH1eJ*O^4a;ue`n5)D5eq^#kTHJq}BQ`T_G8ctcmDQh@o z4f8$|eNI`!DQh@o4X3Q(lr@|}!w0!W8s=mW8cw0%>!38u6Gb$fLc^T1w1!jGaLO7^ zS;Hx7IAsl|tl^Y3oU(>l<)Gn|HJq}BQ`T_G8ctcmDKuQgHPUd(8ctcmDQh@o4X3Q( zlr@~PhEvvX${J2t!zpVxWeumS;gmI;vW8REaLO7^S;Hx7IAsl|tl>#CJRM9DTmAv+ z-PK97`Wn=!QQjG49XE+qzYq1+>LglKjow=2X*scRk~sNMsP|QQ_QISsiN;=mdS7+Y z8kM0&AxD~yxI*B%PPra`?i8e&NuR4h~ zM7^&%i8h{qdS7)CJ?Q>=Uv&~aoP&B_brL7DWn$lj^`9otE^dj3aRgbddG7LsdXQnx1U1lJk%+vDN8+t)bo_JlAS{8WvF*N zr;z=JaE#G3g$5pnddG7L4Ll09dpYG9c?vzKMa{@l_{OtPr|759hFZ}(o>ORJ73$5h zDYT*f=#iwDr zkW@8#8ub9JTnMJo;TqKYHPg(<_d>m$F-`vbQ>c~KG%93@S^U_K2HiUMNx4yBpKY4r)tp{!Wt*>l&TR znMPNyK)qix?QC$`+2FLt&NTYb{q=s$H2QfiI76Rjp!$5qQlFuR=P2tWHqW2Y{29xB zhStwh)=BI$wEH;JDRF+`1}pHp6gxkIIzKau)IWla^lcWYmtYHXF=v0+SD!`3CaC8T zv)sQ3mFvzT*R3$;8rgByV|>ZiP9VJ)#+FW5(VxLo2#c-jZgFyaaWED2;uegcas4Qzr`2j2hMNF?FIK z&3JhgR=H+Bev)R?s75CW()9Cws1rnKbgS>`L_r$stE>|RX{@hu=wcb_1W_8ht4621 z(u^-rr@YdPuO+CNE{#Mju+QawQ}bP#Q_bR#Ic$!Yqs;heMxQ>7u!>AG`sQHFH5!9y zayfmfSd?}il15`MQ%<@*W$Ii(nz1SAW?VCC>ZEQOuhShCOq~ZyGiF7d2TLK!{VyB(!7c&!S7hOyw}`kR+-8< z=dix6uQi*^>&VJP4l9dV*UVvMm6dJIVP%#3%>i@J95RQ^ z5mT$4IjpSfBgFPOtXzUI*TfmEb68oQPMD|6q?s}&&1rMSoHf&ATyxl0-(4^l&5XHZ zX3b?YXBm~%&!J(-o_Br0EaF#lSX_52nH%P&xyA0;9MSldfZq(G&-}U!_FC}RU%>8P zq};gkJ*ct2fZe|Z+juIyfZb=H?7o1W2cYau_KKe@c@UzOUAe}c`L!_SmqqM; zC)6p6MJ)a*+>eJW(&BYcySt0Dn1$Ni<-{esyPPQGM8qQbz%QXrSu8SgpM^SKv4~Bd zhdN)eNKc-CI$yEK2>mtGthdOzUU$%$ibckYuGg80MdmnBXDSxy`3%&VibZ-JfjU#M zh*otsovB!4j(Y~`OvNHvJr^t?*Q0PZ^VAZv{L64ZZ$m60(RX1ZIolEvJpx;hWeJH? z)6U7jC3N^MsCS>2@X2SPG|Aq!%Xwldh)V_hFNtums_H@`j#U361|N=#n_Dd zlc7I%QC5F4^ygaGKo*ptKX*X&CqsWe1a+b#Lx1ju>Q9FLd;qFH8TTjS{$%j>bJVCm z>@Crs4E^~GRQ8^+-81e_hW_Yo>QB}&pI?t7=4Xldo3O&%#n|VUv9M8=2%xf7*;#TV zl@;@O!=AC0rT;fT#eCkXG!^r+$Z`Q{y_rRp5^VLI74x&i`~nPJ{4G?>&k_yRV5jeY z%^Z z{P0=IT6<>E+sja^_$+h1sF+XQVd-Yv&skG3KTAwd&lk)^UzstN%w;pjYAZ|3*M0J4 z!CW^>rk*2biTR?QAoI(4^m!R?5#=q*_LgP5%6lW&N^(xBMJd zFsrT*H&lN)+U7ULT;5IW;@6>EzRKKVevlkv1uxR|S0fR>P~w_Om-o5;TGuEmTS4AC z;Q`lF`RoOiimtHwQ;Ut*ZUtSbSIx}+D`@OB*h>6aL2vrB zoj37U7-dhxqrO9@dCcrGyG@-+TH#cZ^l;MG_qnFu954sXA#>OqF|`6(!6(It8TEZ) zF2~JrbAnj6f?a+BldegbljfA=dcd4^%^8{^ql5;+p!;a@DE1%3^ z$KODmP|Z1?KN@e_WjlvY-;%lDw( z6j;SB2chh;id~pxu?uhA*er+a(ETH=*|%{ zO7_ANEtliwxT!pU)pl8Blu0LgQ(zVAs6|<4)z(?XI=Y+A50gJyM#X&cvYoHPMSq$x zb$)mi>pTpV=dU`?U&T7QgU%1HVjZF?)?ud%>!@DmhgY%Ae6WUf&cn-i%FM5dvXWiH zE-K3|{30V++!}UK&0f~9YuH6pv|RJ-u!db!Q)_Cb;C)1Y+JeP-N6O#T4)7XdMBfS% zVb-vP?%8P`GrLULVhvm9Q|$w+VGEV}%>i@J95RQ^5mUBU!xp+eV(Prs8n#eP+#ENx z_q%3WtYHOxs^8gL!wQmR+MIFCtU1pNzJ~sF&7zqxmrR|PTSNbnEAN_uxo&-yT;4D@ z&29EM&oaaQ8fq1DmbqXNhS<~QOg)+WSz^VjQ0tMijD3|O>_nYq)V~hnc*R*-)RoHl z&k{e$N*QIJg*p?(ufsA53fS>kxSO32etnQ{6|m+y)S0M)EyynrxW0jBZM>aKG%hf6 zJp^?oigy9=s{(e!pBPzRgtBIVK3@uTCaOT6?}J)N6+G`3=(EZ?6IGzkFF>7%D$sMa zt}{^u>%YKQ;EXa>P`%DX6_9HdDw{1JR}?CHGGB? zZ|U-uE>FbZT}PzT9VA`e(&dqkCy|yek94!Te`fZ%Ojmyk#t3*D?r(@@c+(57D_XiDay?I3JN!k7Ij-O{Zi-FGQVx^<-c zK9mQpTe@{iw{Gb;jl%d^w{+{4Zr#%HjM3L8+`6P&w{+{4Zr#$YBb{1Vqji3z8=Gz- z%g>;GD`t~E{~gqMq)qy~4Rs!g_aK=UHtFGhsPjmh^e_XpV&z#PZ-Q*nifS~9H$93s z?dKbAWy7s(&`NW_uUv2zV#BR$xRnjJvf);Eua33GhFjrnp`8y>Rx3QUDNM=AJ`P{oLlBs#ia^-Op|MshTyYf8H6~ zuivZpKUMH|{KhqZ;QF@&Z+ff#`lH@` z_+9x;!M|je?|0=lbAA4e@>_nd{;j`PejC3Qa_)_5{y_DOAO6T~H{Uou@v(}5e;*lm zUqwZDPeuFb*o`;6|NRx$?A^O>PesG&(-pqrmWrEWx5RF~H5T2|6&wHf>Cv0RqcOFgX@dx-543$^&lNKKiJ`&uhr{^eRu%k@SIS^J zg2cGyrr`bjuYw=v;^$8Jc|d*Ez-OoV|9`%ReklEBuD^wUZw_vS`tjE=NAGUp!78tE z@c*~)(n*XqKhAeYxr(0?<&OV+=MV5-mB9!2_iFvE0{_F4q<8(U^#Ab%{`=|R8T54_ zcs6(rpMRV^wfW%r-~}}J*5GY{{vy*K27eU%3;r_e9|!M1|9^r`|CGOd@UOV%yMuon z{2TrX!oLmP6Z|=P=PwrU3uP7j-uUJG3fXRcnfR*U-=UWG1^*%Vk0{^+boPUE@M;vn zFJAIXh*kU=-vNH-?{?|!qhlz6 z_g;ASf_Ex7t>4S3?|xK2$Qj#VPHA%TG59|FH}?g%vwOG2&eDrKEzpyYM}mifhlBqT z{4977Z~hxR>j9oq+#7s0_#AuuUk_dlJ|Fzo;7h@Gc+!&$?g)Mvd?xrx@K3>=!8d|0 z1iuK*2mct{6Z|mvpTTc~{}udw@MBgIS;oQ`rvEbL(y1_Z1vEbfPmtlfrn5oh0>$7b z80~Kv=}!haVe&=JG2DnzKZF@?3ciaweH4ef8S{P|XSy}`MDVNNlfkEOvA+oZGWbuy sqrvxr{~UZe_^aTrgU5pZ7X0_%tHIZTZwB8ACix>DO|jD-8x4a02fl)uPyhe` literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/Silkscreen.ttf b/vendor/szymach/c-pchart/resources/fonts/Silkscreen.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ae4425ddabf65d729318736cab2435f47ebe45fa GIT binary patch literal 16172 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 literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/advent_light.ttf b/vendor/szymach/c-pchart/resources/fonts/advent_light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..514030a885ddddf504fac6b4db1aa50be7c3ce70 GIT binary patch literal 45768 zcmeFa37i~NwLgAuRd3Zxbyrt)SMU2W(>>iiGd(>$GnttrlT5ZrAR!PESppLFT^0dF zc~4nYP*7x1M4#wGL?gV1AtH++0&YR&sfdct9TZ#u1x)wvdv0}40^<9>_j&LC`TXAJ z^Q+|Es;;_Kb8c=MVwzIycIjM-1Y^&cF#xiR{V#?~VlX=iH zK4b152X`};z7FTUdCa+IXPAMOto#%z>9{kFKh?4ALv6-NC*T=p9e>LC$Gz)KzrwT1 zKWA*m{U_`=^}JVHAO0a@-T`#-j1!JMddIJS^V^5fb{hAOpMVq2tN6R|`)vF!oN(&d z=WTyBieo9ln0)Zb$G+*b>RChU7`t*KW5LQPryq0l^_SoAC}Z#bIPUrMsYjo8hV(n> zP#mLwEOXk?ryl#3GjDe=b~A7wz3+_E&pJDP#clU9_D_JH|MZME9eV~kS4IDxM0*<} z+)3Q=P%U!lcK`GXEGqvRXV{nCHvdJj|H#q*+TGO+*?ws|1iuv~(OGP`PkOeC_sBPQ zcXa}`Uy3JL*R1++aprM$9&@sjdT(OPjr)Kns+i0_#fO-k*`#}Ks%Zqd0VKsgj8(^2QGJ65f`x7(S?{WMLt5Bb)@Bnc2CiJ(#{9;eXTUY`3Xt6i3 zz+$^yXm=2E@}cgFbdIhQeCz_e>(eNoWjCU1BKBH|rq0mfgk)$FTm zUX&Nu6h9s15mx3GvMKhLZihdNaT#J7zlSBo^FIIzJDSxO-@ls;;MmVkV-dVFg~Hec z-Cfpk_eH?SF=oRAD?bIe`A0S?J;p}F`yXNhcz=W5A)SD6ydAi{lV$rwIv+6OH~TI# zdgly&2g|ctSqyM$>{J$!zQEG_48U`28^3%%5R({#}*>o=fu0pxYY&&vU@xPW0=u%;a~m1nYoCAH?|G z3p~EI+yNXAUtGid(m5>1FGAa0fZ+?EF%$_@1Nco*K7nUE%hFPn)zO>B`Qo-7M@#y@ z*0;scO4QBuoOT@b`X%evH#w)4wDtCjH;GeL+x^e+Dm7kWHLvz=T90AFdu3!Q&IbvAGOK?-M=zF(@+Ok5Vgj1)InyT)>)|g3iO4EI>aKV!z_y0WU=nc z7=bwIC`+J@u_Wp^OQBA%boV8eWEs>cmP4IpdDIzJfON^SBI+C~q0X~1>H-@;U1WpZ zKeH06pf0m2>H$_mJ;>^)E35$uud-p(Lu>?fjg6wNvu5`N)?j0(hgl2t2pdN|$|g`Z z*<|;5HpZqPfZ~^%Pr%dI_6DJ)T`NnsMoNKsMoShsMoQBy3er% zwi)$$b};G<>=4ukur1wZ*@0{;>Wyq0>P_qos1IU?q2A06@BWS*%#J{P2s;w>7PcMr zq3o#cZ`oFMH0o{a7}RfIJ5V3SjzxVqJFfc-JAxgL`bc&H>h0`A)JL(Cx=*vC*~zHi z$WB3h4EXeEwu7CH`dD^G_c!b~b|&iM*_%+Gz|KN_B0C%PN$i~Nui44$T-2wq^H874 z&PRP3B+#$e>Fh12&tPvweI~mQ^_$q+P@l!#-u)%y!9}RgVgG>oT=ovsZ)Wf8{sNly zV$|oeOHg0HE=Bzob{XoovdguVx=beGU6q_b2TA?Bl3Ez-~c(E&BxO4?e zvIkJ_WM4x4IrbpxJJ?rH-^m{8exLmd`zq>xWe=mii#>w+^X$>?_t-A>HPm;rucO}0 zzJdA+>|3bsVSBp&#=giNLwzs%HtPG>cTnHYzT17AJ-{AE{U!EqsK3mWA5nP(Q+c-2D!Fl>G$t*Vt32zs`P&`Wx)uQGb*Dy!&nTE%po4 zd)O~gKgNEA`rGW+sK3L0(|rs&>1ouDvu9BM8~ZKl@3G%?_ptA?=TQHE{T}rb><_4a z$o`1>N%p7ix1e>NNBv{=0_vZz7g0aO{@nd0`zdtoH=(ayM*TDP3hJM;zoPyH+uQvH z`z7n3{uS$@{xxT)f5UnA>+ESRp?-$TsDI1tsDH;DsGsG|?$_9J+=cr0TtWQ@?neDb z?m_)0?(IIxp65Q)FK`w0i#&k(&s^(1!v4a8s9)kb>X*5J`V}5R{Z}6DKFs!V6Lp72 zPop$V9F3!;0 zWpmOS?-Jx<yME}#K;bW1i|cUy1)V4GcWosRKtrGr0hiMw;So-c#m#^VL+`r*>T<1gUbQ7^Z4*w zh2G$HqSI(Ac|a(FS3yHIAK>!Ic#Vn&IPp$v<3IzB004T)0{(q)5mchbcoWMmm-OWD}W0)Bk=&ZTs{epaQQ4LWpiRo zy#$w=+IpNaPzkua=ojINILz*|0kaS`cE1y~CV8SKf0$_F(f=ngp66nK4y>W7&fqI1wyA-Ae-OS-|i;bS< zrZz4Q0PMkqMqZEG4Hy)~P5<0(R{(%w2vr<90|ANQM?g^(F9}5z<09tWAp%mRSC#=gc7i%6y&$lsRr;oFSYf#Y#9a zl%PO^H-JM|FepI@DnUTzLpL3M1zbV0Cm1BM2AxR3AOvX8=7H$P=0Sh(Y&%d0xZFY6 ziR(d&n{5h|w4WX?@Te;EdWcj%$e*4m;0gkoAmp%9S5S*W&*eY^Jstz$@v=zR3%2)) z&ExjsA8o2ffjFZLcY0Kx&#PHYydJO5>oL592VW5IDuy93{4hL0j~{Z)sd+pup$`p% z$QpQ-4a4Jd8a5w9KQ^z^P`nUYiU&|RJ%-JN>qd_{+<;v(@H~OXfZGm4f~_>g4IxT4 zh%7q|ke6ZidR$>Q4M=}bxZ96Tdw>wPS7q_23ieRN=JTi?A1--30k6l0chiQJ0l(j; z`}&)zdc)$_Z>WGV9F`bh4tov24pPv)UIk3)aD>BPQ^Kbl4uh(~;4lv%$L9{aeRjJO zD1-p=hHY+K54$aH_CT5GVc<@qwwl+08sw+DyY>ZMLGf~r zhE`rMhe!3ZWZVz-@QY3L`n@VH;ekGn5AVi?JAGOppc)qFs!s)w5%D7s_NzWmBqB4f z&lmB9eL=`ISIFmcljwCsB4AUG2S!08;&UqzyWfU(KA-B2cvJ{upwQ`4yb+rl*CQ5n z*t}4Cp$J+9scp~);RyW7A&?32Dsh++L7yTH)#r|TooM9riO2f5H;7g~zsKWM11ysY zFtWjx^1~B7l1h7MRn5yc5 z_;oqsaj+>U)ozQ&{T_GRuGuVfc;nsx3^O3n<#(%by9d|fUW@8|Hm}!=`!&!4wvY;O zs{$_5tNI}ipmV|Sai>p>I|F`C%IES!mZH$ceTLVks$dv3ps{RP16=4Vwt!Fb1wiKKG8o07fD1sz{ZW6&ulSXSKk#a}96h)kJ-9T23!U`((Ji+> zZujnk%b{XSBLXh!E#UI2e%P(1*YAh;7I3-a02XisJZZoLy8=zH0j>}clLm?i1X(es zgY9*(1^v1|NNxOK&97+!EGJrqLq;$bv^EVb18H#_O5o6+PRk6irnQ7-YVLqLrfELX zhpu!Q;zw0|4qG}6x=uTEyM+#KS`ET5Q#Do5Jb|>shwEvK3Q@h^jsE~AJ+k^H6#j=?|&^bo6*L(Jz3i_Or&nn7)}h#t^& z&@<2))bxmH8Yx3;Aw3id>3MN%WSyu**{d5h|un!_JR;qkh_V?3zf23SlthsTyKPZwGL#Op{AP@|htX?(29ucvHgOOlJj9fev42MYBxfERw#iG%0 z#sb|8nEL2Vet2^p;9KO$Bkg?4^}Hsw1CD*!>t=SET)X6hoCv3bM1Ducht>B%5|MuI^-9A%^RDD(ScVv86t!xW8@rV%j(T<%cFOvDqByajs1jKpH$ zib=l`rC2mZgI2~5QY9VZDLiAQDs_xfy&E=?6K-U#l)FIFj)hc=fYD_nE zujvn0oWV*ZR0&#CA94ouLM0qA(}cNn7-|IgwdaFj6M`RV(Bbe`JfTp<9W}Lj2rz|x zCN+h+G15AAaSTK>!Ny19ED%VDEgDXQW1>wu8j8k9i7Re198ITE@e)BE$G=1(9;pd5 zqy`g-NT^n`GXPnO3`TMhf5cyoM0FCs-dfFX1PmjnIBK;>Fi=wxP7t(d#DkAsXf47$jjGKtIKEnM@KeBoaydOC^)B;Y1u; zW+;`6nZv_&7LUb-V?(h*ED#F}#^MI)dEfAGAQS|BxShkpF+&@6r<_sRlG<=M0bv}9 zht-%C8&(Wl9}ZdE9C3xhmEl-2R-mSZs5cgkVcIzmipG7=mM{t(+ORJg9rh;T#yDV# zMtj5-mCZsJ_{Km-5*fB)HpdJjC$>yH7te^1E2m@Ww6HwXL_A$C7Bh`3woEpY&SrC| zi41MU(Ofnao0xDgU}YlJOjS~PQXfubOcGOpiHTs;h(=A1YhnU)J>ki@5=m^i@I*B0 z^97=*n3gh96Yj{wL~0^xadTXW#D*u*ne-qvttM5}n07Xz$&^0;l?6lFnD8f(6KXbN z&c*}4bZ=0Sa=a2lM+&i6B3oh$t4ho?OJd8XN~uDcwxL`ymnQ}7gB;HfRR#;Kg4jy= za;cP^E#$FPCQ9XOa(33qfR)+YWUi45Wkch+e2m0YaCX*6n2AKpr_9b~qvouyq+|&l zrO0fe2(2E^CH1_SoAtzIXLGZ0LG?=79Z!zW=8L&{26!CK22f*q)=Fe@njX|(kGjIM zflOxBUo6B{rGvPv=TKpjQ;j5c1Ic8jGzeE4ldWQV10pN5C2$5u{FjI{B8H8K##15UZF%jA9z`h%JZ^9EwQ7 zSK$?3ftbNz@Zz5gzxvzY4Zjo~?)Sj6{XY1wKM1e$C*T>r6JFgf!58}|e6U}Kr}bdO z77jrS0!aag6ZsJPDIn6)Ml543A{na@i&#Je;S_lGPlb>FGI-7}hY$S&@Ogg-9`76A z(f%@gy${0cdn}>_N5XUbuZTe~cV_7+i7 zbtXK=PQ-&n^_ zZGY0Qfck-j~LmI+~N)|`*P-fLmxwz_}t@&-4D>7HC z-El=`Rpx}FckHwk#U2-qy<%GxFUW9;OPC{#qNL23C>=7 zVp;D5Fo91t8Pbt36wyG6A=0dm|O`MRS5{_1O#&eA~peoD*-bn!F)$p zPab(vSRpR)N)O~Tz1oCbATXZdm=sG@t~Ezf5*<2=&Cyw@HCD$-jyhk)rCEs|T^>^h z)=vNIiPF+qRQAQp&eNYtHnPERX*l|EzC2v0g&({#J61IK#lbBb^6RF{(sO2GRo$#t z%YKhqn66bP(%z0%o-77==Zji-4roCZ%}e}z>8}7Rz8j+xM5oYOJMQYM^o0Z!6@#bjQ6tt4hsZwZda&$#GHj#kudUnm=hS{PwoH2N9X?}GY z5#Crk8lD`@?JSHJG{xx&!rwHsZi1gTI60W~M*Mz63E*kZ%#5KM@M@xS9gG=Sr6wrV z4N7Gw4p6HX*Qr<8d^wM|=f{KXrtaaqGqUOTAjgsEH&1JCJn@v5W-pw*kDqy8r_B%R zd;)aSgBA4U5%0+09rxoMKESyYX?u9Qijn{YIQG!1@EgZIP=y2g5cWRolQf=T;D|8f z0v}Odiu(;>S&W?+wHCiWH@Ttm&AX=#olG_|qqE6t-p{7gE<)#lBaSzW8 z2KVkz(o-WCj}rQHx_}!GcjE-nryFmz@4;KC-}F`)wg&B;ct5>;kZajcR%?dxT9%*M zxtp);{POnOc|>|{sPnx>=U&_f_{Fnrhk!r)IN?M_(YFQPi1UIUt@GPq^AkVy`T~66 zeE+w+b@Bd-VCfTosp#`(dU#B;rMtm!h&Q8qK3t;y_pwLVOza^{1D5owFbDu~G5qA; z$7C50^3S|3HhJfB{J_^o>2WInD$tnezRbTM?Zph{`ge8>p*7)RyAIWs)p zYPP5l2aWFIo+QWUg_`_`+ooo~=^ot^Ts_%XTGo!i7AP+rK4{RbxwX0(t7d|?-FDj( zBkLxOn9J+PWMaef)l%aNjTQB^%>=J0ewP+dv+c?OjV}n;N&X#=@ljZLH!1=FN&;Q9 z?IHC5jsiakxE!DgqNnB}Ii)O8>Jich#Pf8>jDG^)uZg)Nbz&3^2w#l`bPox?{e;Ubf3U^uw;3Exwa_0lmbDi7xA$uwJnnTGjb{^jCWID-X;(t7oL=gjy znt6?YZ=P58#68=k2c<#cOJtg|iv?dt_gZ|tiTD@!)QtUD@OAXhLeF+@7JS`%2lyIC zX!jSiJ6-T~^jT_m5%LxvroJFM^CRespLp7iMl@u^rV?P5s7pQgT{x!mDsVu1jqu|ES5JfgRn4#^2w-I|qKTI^zu6pyLZYC0N_9vZx^^S$fV94IBYFIme&MPfUKLW5NRp7au#FxG!UI=C>&jiqG8G$}S`wxG%K zSE!OgnKX8U>xF{ii05(iKILw%)O= zb0;NJ@Z-LAoYmJj`ucX@$<9m~Gh!w5$tK{pG*w$j=XJc*`A}nU5KXah%BAA>0Y;O7 zC0r2hgkKlnVt~sd;KOqaz(h70vS`r&uvw;<6HsL}fJla#HX3ezGcprH`1{g7bbcC} zi8;KE%i#d$X9Y?D#ZWeP{y55W{!#!zj6?VhG%g%E3^q18Q)lNB3tD4JeB8jhQ|Lnq z=aTpx#c`C5aXy3N47yVVD|0P4xld{eDS#HkH2TCcX%vn^qmsleF`@_DLN{AS;Cc*C z&e0pHJYWf-)a88Hkf6+(7$)m~*o2msh8lL=qvmxOo2}ST$P+@qQgw4XZwx0}W+3JB zgb;}yKV@8cZm%Y-A1YPPoL+PFnuFf6oag5=nf9P*e_!XRtEJ}-|HPW%BPzK`BdULr zAMicnz-{P|$LI4uTF1=+Z~|>_G{Lp~HgDT*Z{uywl zp?8kAG!{`iX)F646dV)9#{g##OdJz%hW6}+>!$yUpee3Hv>lQk1&W`1RJeX@{`Q>XD zNM>XF#W)N7P!!|8isXC$ywDHU`R(vMEuNP;&<{Aj*pBQztKCKL=aSu5=-$k~BF_U! zIry>;+r=h<$vny=kU-{8n118f1I#)gZaf0BVVtRhf=I$m^*Mv2WwVFVw3r*wEdvbU zQlcbMANl_RJ*}=!oHAMCw*Mqq$@9PKG@p@I{uha>hnug8Ij4o2`yl0rw_*^m4?~u? z!546Hka8j=9s|t#OAltvG#!{}I{GtBQp*luGEHK?7G)dCi74lzT#j-*%B?7OqdbB_ zGflFw4PZ4+81(|1NWtjwBPkZ>4QpCi!c>zaYj3uhU-RveR#;v;{q_2bBlES8BfEMz zFO`>Xt{r-0XZ$7Uxx(zGdgF~7#{g5P`&oXM^a|w2I<|HfTL3&|lm)!my~moOm2sTJ zXy*ZVQj9juT}II_k|gVigGlob2O03LQ@`w?e69qM-Je ztIk_wQX642+ywjZx2a3!wBp(&mL(Wjmd`JXa0BurHZ+k6RFk2;5HX9xQMV*TT`Dp- zZT9F;)+krXId0To8d~#EB(SJGB4{ihir7N|Y5SsBIXBhF8L~@_`MDg7Bhx0Ft;0Vc z#`+ zAi0B?_yLj#u&THBB+JW?;M~YDibpObIExiEm@t1e8<>-~Wv7G!MZcwMYPn|^2ZcwKi zeh@dP(+%o$gF4-yPB*C24eE4*I^Cd7H>lGssB<~sqKOh2Geb|pq2j>khedp42$7K? zwhCnn%5f;?qFjP!6yru{1N zp6x*yiKdEC97|{F#jI{rS}SYiRjo|Cwj=|*%UJ*P2mbH!{d9zQ+Anso4ZzYI$_BtU z0jy2n*rFOS9&;Fvx&C;}0pK}|#~j83OM+0gp`3_vKFZ}N*Q4BuayQB&C?qZB@Z>l! zH-UZ88jm8zqX^zEVmyi%k0QpSi18?5Jc<~PBF3YL@hD{p9k0QFW8_u-} z78hJk6X64hj>$CgfsR+>=`=b`INyXcGRw|(sSMp{TJ|i2``-)~2B-b{1N8)DtgTw( z3b;;-wPVQC=zK7ngNcKVQJZPjozKe4)~lm-jVV0z{F^kK8yw3V10 z00L6>C8^xh$>tA^pV(eCe&YD(LnFtINYCBZx$3@q`C0ewU3xn|?lVGeMexj@QFX)6 z*6QeuZjjpQlilSF@T^idj<{*<#L2bhtb455xNzB;n-3TLI`DSBcCxVCiC6fr3OEmw<>*!!p50AH<;0XTbZ%@Bkkvcbo6A*k*7DF{`qrbep}vhqlLNBT3cxe*auAeF^Etqw!>olF z3kYU8DWVy!%9bx@e;WLbR38@$eKG|esMhLwbMthz9v_)2XQvwpOlYp3TA^_Tx%+m1 zd!Vi+d}{-y-x(d<(BgxgZn3Kw=o3P%YJ(er+Tv5-j zn_r?kBwy!zzDIH(dwSJCeqnxEw{tJQu9J{mzQfe&*i>@->{E`&DDH|^4~7HDlV{uK zoO)zhQSjR^sqekMfM1r1z6GUWwjhxj-9{!7$&clvd;i;(As~g1*pj&hocE>H81&!* zQdj*6Pf*%`Y(HOu{Js(kcc14U;ICkOC&(X8);xLlT%ZH8=tFx@=p2sIbc}r!*T`eh zv*ULoOT=%zizc&}{s|w9Fz|>j#@m*82aB%kEag78}VJMpIq`x#Uz+bWD(rPT34RyW(f0AtXYTAnNEi-d| zr#%xe2eYRAJZ=m(wiX&mr2BH!74W(Q-3U14_h3Fl&S3{z^)u`+CgQyG!s7YuEu074 zJRo)XnEW0%p`74hH_7kWSH;O*=|{zair+mcznx-WhL?w%rV}x+eb45vV7&28>s_{A z0@qd_oa|&7HrQex-rMT~d~wLtAsYcSENzl#mGCBH1suH_$u=|r6_(Tv=++&0(hg|u z9njo6pt*NIbMJuW-T}?M1DbmWH1`f@?j6wFJNlY?2Q>E%Xl~HRiIB*|(|Kg%kbW*= z{4jQ9XHzAAjPO>2yYU|cM5a_qFbIvZ_}^3F4zj1XIV+P72L=|0{-&GopC|#AmK8_} zNs>NbI0McD597%-1GR;AIs^j=%WaZl9ywH*8OY5HMY%M;Om9Wph7ugqjVe;;l+K{t z9m$34K6`vLui0fIRSpMB*$9tR(7+d0&8mirp)&Fm2bYx6Q{g*;CEXQJ77k0qI8ThM ztmJ3L8z$D3#g?{$fmC}i8XlO)Ew&g<}{=mSGz*Q{F!1sm7N$g<0d>>i{p3SA`RVfCd>oymP1<0)3B|PPrRt5 z$c0L-Q_==1QI)f7I)SNn5BDm+4vPlQfUEk-{Cpm}Q({xn3-CPNhSc%P^DF7pNwKzg zeuDjs-_F-SA`c)su^V2AJjT(AMD(2;K}1(zb2*%CC_phN4R}N$4bmW0%&5XNbeK-$ zN)29Gie-4xq0wr9R!Cx5RCqk&P5C#6^hj013M-c!8qXm4`e5zXv~Y7IYQR+ri7RIY zT=uLhYB(hjnXY zFAUrx?UYtww2wk&-8*&z*iC||j)!4S%*DaFv|kE#CiuyQlL70g*!O*`Uf1SQx_U1&+m%vZVQ?p_vuo^vYCb zHW`~~6?maF6C0nd957Sl-_mLW3BGbRv$5_k4h{s5qNR&m@`YV)|JtGBXXjL3Mk@>_ zy;^l)YU-@1g=)a8t*m_^I#f(3l1G+YvU|{cVsiax0?P@pWFR?qz~quQO&&0obg99i z)#G-rqb7$M)g7&If8cr23sRqV@06}ypC4SMl5d=n>)+^Q4d&ATqf_3OIFmc73rhKbEOxbF0e9*_k zFs<=`7fD*t9L6q8bl$8@o-$cK`dR)h#Ev+=rtwj>MP3hXK>SL^?P4B8K5x3mMC_~F z*e>7p*F%8UboWE-DR~1d|9Q5s8>+2}*03djpT0kd?9o1F>~EXWad{8>` za-F9sk6F<3G;b3{=Pi;Kb_gs!7%XW>Gaydl7kYvb z6-#GT;Y#9xaMbONg#Fwb&KjK7R%1!afIoy3?Qkf-&!k&$W9RX7Js)=?m%#})($pZL zeWwP}$%x{LrZRz(16d6xlU|siXcAgEeUF+>npoD9NRtT$oS4AL_kj~9?1V#Tx=a}v z01fa`q>w!!v>QW-fbfDqSsw-ZNBEk#N z)koL^@&YIj^J^zg_4NnET}2$5Xsl10rZzg>#0(pIsU zJ-V*onGcF*0zp=4h2@o&@J!N44q(%Ry@>15Jk`oV0Ufv68c@@}J#3qMNFrWdmL=i`Gz&E`-r7Z30ygX8o|jplI+<@vnG59NQsMDNvk zK9<$@+B_f23MkjfW(SoBBZ2q`lV|dpt&I*%=c4tPJ2{n%)Iv+fC~~2XB{M5hSY95R z0-HCopDeX9s>5EuN+74cdEo*vsvazwa$|+C$Cai;9|_-1TEz#XKLQ%dE}~JSSv!p+ zVO!WmqC@cX0u zP{^kNNZu8@SPieB*>Me{-1o&&#E)jjL)gbb0deA|ifBl#oj%6MzS@@=_4hDV5(v{F z_x=N3Do9<)%#L-=6w;GmDZr+j{V&^;cL8^2AV=gXvMB?u zljDEarfgCEF~0_^VX`y-XJ@4re|dpjidzIJ*Cr%pj;~AESfgjnT*-7fO~aztfX}1# z_xxOMAb>TG{+H>z&ksq;;ZwZ{>l0=mKEZodC`8Ps75Gl^4L&QUPk|Fk0ZSq+^=*bU1R&W8I%J4t?Uk5r`C(o&-?n_-kna1Y*%k-$TWFR&lN7V;%-DRPxIV{a z-uXdqf}LC_C0m10>7KnSA@F;1>j$f|rJxpQpWfd4L~l}kwO{uphe^Z$=T7j}O7ND4 zE!ihespph8LvqwX`9>6OrJN)Bav}+F@{2mrfDBxPBBvS>T);1s%y#9amo_fFbUR41G)4n* ztWt=VP*45wV^96js6QfGYq3uuO4GXVQf%lM`t}_pUh(@n%dz_oBj%tZ=Aa|ypd;p>Bgm}}0)HF_zd)hr2>JL4v81>NE5vQIr#RC%_GDMlzGB57 z+u)3LpEKIzfCgu@!5M9EMjM>b24}Rv8EtSz8=TPwXSBf?ZE!{#ueY8yji=IER%1Vm zy@&#k6Ps}F?TtC%wuJ!uG;kCFRju%yCx z!}4>N3>(gA?|eB}8kuVpSG2P7RvyfbR`S8l1Co{<7eU&hme4by%UxtqoenBJ?0)aFmq|CDsAdkk-#BT?FQ=E~;meOoU zn$~bEV?RnWM5RY=8E8DxSD3XWx&75yiLcS~y>Khi7214LHr#o1Wiz`_!dEVeTZe{< zrAi={4{DWAVLp`@914UR<(Sgp{}!7opBr8=KB60LzdLMtJ!?k3muloA_~b_L%3>p` zxMTIgNgC}397-~=AHC+Pe}IuCue@BH>5oagt`dEf$GGSQyeUWRjN z>Aj-u2k{K@lwFKHeUY`$eVYG9hJFMA&p-=wcf+YgL2%0G7!%w^(dZSx9Yd@-K(-%< zQ1IOb@ZARR9dYai@ZARR-3IX82Jqbm@ZARR-3IX82Jqbm@ZE+!-)#WjZ4i970ngU) z+)VGe;IJo!?imAzA&ze8o-uIP7&=Hx>c+rfW8kncaM&0)Yz!PW1`ZnohmC>5#=v1? zg2Sla2h+ID3h70Do=E{%18ar|zyS6Q07I|0j`|t3l#n7!eyA(Ufkz4}C6|rnSiabE z15nyISu8ruDSOAlXUBh&m^`>`8$Wn5rKie9v5_2GXw(;4$>E|=PU&xwY@HYM@{+-U zsj?oePNq{xSGg&@bSxVmo-0>ZG$Ogy($vF^P1CvD^rl8>b~x`1A2D8C!0OV8h3eSh zQEy>*_M=?7B{flr#VQl2;&jzCtJ8b$4Gx08X0d9xHe0MOuaLY42SvJj;nyFBzas=s z{)eF@iTd}D{G?G`E+flHV;n+>SVE%#Sc($|qF?~*>?s`lUi^Ucx0)wJa}CWCR`Ud! zi~N6To=LlkGDE#2ggM69_ zKeEyJRfBj3GL}CEKevOuKSHu*9_8(@!Bu23r0Ef~7{v1LJ4thTAh{z#j=;0li_O82 zgnlE5X`f;CcF5KezG4R33=8~4|AW$FAMAYZgV*uWbq@5~w|6a_7A6|K7tyh6;1hI% z=lpmNMFCahg;;iV7~Z8BF6U+4dX;$7d{hmFlxfA?e?bPTse^XusJ$PN?(CU?zyPgE zWb7%}SV6KyC_$DWBE3Yh)4oAh|;R(?;^|zrA8QMsH%VZf#?;Cnwo+*N>4wyp{JF>}7VO-MX_DQfAs7e0orv@Bqh5BW zChDnLv^KzpzLNvmDD1;Kk@RpjSdiWNs%3kh=8tt6(#I_t1MbS*Kl58LE_wDo%d_CJ z@<3dWU#`CVazTE%AirD?KrYBH7vz@<^2-JJ<%0ZjL4LU)zg&=CF32yS7Q91VU1AkV zgwz2uu}BpABJmTg)Ujlkm0PES8VG4B8j*!YHaC3ZR+>P`iA-pusqdG{1PNE(rmmc> zOy<;WtBdQ3yz`LUrAqtKHf|-AuCsxY}!6FbW9Ob10AI-7oxQo zj$<@?G%%W0?z@!+WKAor<1|JQV!>(9Tmnv(G4k&TqJtcEhTvjAf>h}A#<&x%DFjaW z0O-$5RpJ2|+3lyh)-6HIR2L!B!5ew&#;KJ=lHT}Gwki!di5DpU|8|ULoGsAOg?+q0 zeWgqd@>bFwD_bo4oZL(l>LZ)Yz!PW`3^X9KgD5WwsRr?ae!|ZJW2}Y9j$;YdFbS)j zAJKVW>eMM;!WS8e1oH_^_WlT;6yRs<*u8^S)>n9DdN|EJd`st3wa$CdAG|?>Zco@~ zRSo$VDNb%9PoBv8d$vAt>O|cZezr6GJCaw)Zi&Ai_COICKj-XX(`a0x&I0=-bk4Gc zX!S`+=ykNf**>>X3z|`pzMu%^2=1b>njs2I2p1$7=a$=z+_wyksXtX5TdrJ4SIE7^ z0V`9e8xilt=q9}4z>IUzj_T`H{{F>5)C;+SzU7$KV7L*>&!zA+MNG3?(ZOacGBOYi z6-QI4Rw>N?wD-&VMz?Zq$n;2uA9?Y{&G_h6ID#((YRoMFW(uv>BV`DM9$G)9M{dUcc0B(uCyfkECKNoa*mjylK&fz0ZJY1lHe2Trc=dRw^Niwiz1YRqqF#b~ zq4RX-xA4+{iQq>@bu)MilF{;@>v&iOXyrh$a1moHC>}x%AQ}?~j%mS{6E~BEX3ge; zXtgLOX)Z3I3FVWJ>;S&@ee6kGR{}cG|q;4M!hb? z-teV19pB$_1)F9f=y<)8=o^Q&cm_5>3}fXH($um9ii=jjGYBHnn5OX6>O-NUXyw*9 zG!dtuku~wPh+SB#{{vZnZj7OXF_cIKTJt~hT!=aT{#t5dz11^(+`Q9~q|fwJ5#%vL zU(XV;`b*Jw%0oiF1lFwV!ufBDSUsJ0uFV6)3MrJ^e40f z4tO`j-U=M}KsRscn;imf16t1LYdHg#4QM$7TF!u$Goa-RXgLE~&VZIPpydo`IRjeG zfR;0$<;dA?Fhp5sZ3M`J)(?tIEwV67NIIfZ8ZW|x9x_RmAY$Iu%KI5p+DxwMPFCV7 zSMZ4!@^=OK^L1(OlbM!Qbes6hjCAnMMPrI%mWaP!0p1F+8ZiOokAPW$e~X`he=>~8 zt4g{^#h~NkM&J^?@;-5|a_91pqPkw3zHkN-gfsepE)T-}eJC z!W6)5xu@ueY&^@G(1!K`3aAU(eTkU_R?xhsVJ?Z;{q~D_2?1eGK-tQV?|fdxikgse zQ0I#@h9ljsbPN90#5g-^_kJlw`wudt6~>_-DWu~fc%o?Wgq07L#h8p>*!qJ;dUga2 z`+;B@$TH5Afh{^;!Epsx%UUuG0MNMgL|hfC3;d8i2<2d4DM=b8lNmO}4y~kH8p*+0 zAl8g|@kQCXnJWjB?0nS68)bZiICD@fmXGV1jg@Ss(&UE?)I)Yz#n&^{bfoifet5MW zl57!=yaiQw3##xIRN*bC!dp;< zx1b7dLACEKsKQ%N6|+gQ8-=Haz86B0!H(zAzAmg`a7J&oZp!d_taLT{bj`~oXYa$}ed6N` zs(Hvy{4E9KI*@e7;9<=2z4(Z=GMQ|8nwMHmDpKcU@RP~x#hwB#^qb7kJoaRU5~UOM z+pv#dPZK6GqiLVPp3WCwee$Lgb}h<&AS^ey8HSVjAUvkeS)k~!Ne+Jj>@Q&dJso-$ptcjmJjfJxk{`J7o(^OyM%l&G$*sdX_z?^x0e6b zK)iCx8{fFiAIllFT5Y7%p&yp#K~kuWc!uMYH(M^X&NE5tPZw?pKG;G=EJ{1&Vr0@(c} zX5_?_2J=Se(aGlQq4;REP0AlyE2i9rTpr(kIH5XJNycg+B=}=dh%=NLEU)i85X<2s ziKRjh_56aw034me!qK@Ww!D01c*WRA#)Iz}+kJ)`3q}2J+zUi>68WB|A+>8I)ul`TBG%!qVq-_Nh3AB z9OS;$AN{-JF0Z30AJ_iBsb0{x0lL3M$f^(S6|(9t02_Rq@8(5bYZ6dn=0WKqv_6NR z?L$s$ar#;HgMgGo5c$_B4s6Zht>YxllU5^_gun)^Yjr{D5jza^d}OBhItEcDXq56O zK)mG=ysh+qA7u)W-O3aE^&zt!Ew>IkBELF@4_WY7pYb*-)G*u>58p~ z8!5$|S(DAoAW@?-S@8Swla=e{m(HvPO~jBt@@{znVG^7j=A6iF`m6LYiQHdttdL!XD8#YwC?RjR-17*o zhew}?W*Gv5v_AtM=Au1)qKozkTwgDQBz>2J{Da2=i{}#-DOgN~$`-+etI&KGTZM}! zlU{^WghnhO^{FQ_t(@!EC>(OGw*C`M7S-0@e+s`J0`R1-o#b%r8J-+(W$>*~EoGWX zQ;j73YAWJy6bki+M<&KwY113gl96yKqMAv+AD4y;g&GgFG#^rwP(FIdP%&TcynSqZVsu@j?!n)5@YEZQXpXMtqFd;y z<3G9xo{6v>m^a^y`N)41wmYdVcf$L|)(s?p{GQBnh9BVk&z`f**TS7EJJu_ja z5b2Ed{H7)@GBl>lJK?dSwe_trE16K3B9@3UvE~Hq|2ht@#S zb;A>amqK`}VgqB*S9zh|w@-*Dz<-FlW~=XV)-i*Dz<-FlX1uD5t!v30U(J z(8Lpv9TTwTCt%G_fE_1b%}>CZpMW(#0c(B&*8Bvl`H8+YKLKlgLRj;(o^A{%C)1~Z z4ujU}BY4pWr`|afz3OL;pT_ zuDz<7jND;M@dj( z5|o$(B_=_MNl;=El$Zo1CP9fwP+}64m;@yzL5WE`8vRG#`cVWr?%N-9OkdT>;ceu- zCi)sA-Y0EJo&ib#NQ2ig)2CPh8)fXpA*SSNCyk}3=guEPUt5u zCA|3a9Q$W4`TSP;QgB~Blh4yjXu@BNzq>@{Uo%zL#!x(MhZKeO$Rwh zDgWI0dZ*hT#2;0F_t_4s+9e1?583Poz4oMGxTJT5<8Vjdj}0Ls&hm;z)R5cpw`UXL z&zVZh4*GrUze&II|G&^L>^0!-t-xIfHUrsdu!&w5J=25U)L9XD8WU~g)iDdyWxC&9wfWw|K_|pj=%T$65I5jfO+KAzIOT;{eK8E!bm&@ z-L2EK3xCp5Xd`l5SaT(e8-42t`bS(K)l7+JWM}h|aL$l})eM6L!HwK+n2E7+1kN_D zWR_-9IsS;klr1R7 zp`43y3Cgu7pFp__U* zl#;HGYepo3Pvw^z#ZtXq3cxy)Hdv=3VZ>&NrJ6TPZYbpS{0V=V_a#^fz{m<NNRF<>MHjKqME7%&n8Mqb+4BQank28_gjk(j`U9sOQ`{*!H0z>}9* zSXlXzrjEs~c_#&2Fa>L(_Kdy(&Plqp($4VJ7zXtxXmidqPE6jJ|%ic7@ zK0HLUx*2&KpW)X4zBD_Pf(zuf6?Rz|BNOhA4E}H{ehi{aqO3*PhH@gx`6!p8T#s@q z%H1fBpb)LnOj|_zC<~>ZaYM|fBW}K5rh`m|6f}%QW#k-!()my?uVyfsb2x&U76wXe zTyh1hoZ+Oub53A37YYW{kmkX{FMRzYMxGZA?JeF`=ZrOu%i(2czNxhGgI&e}4 zPU^r(9XP22Cw1TipX5Th9OZhHTT$*tc?5-ULXmEAompbWvI@vqN*vhx-k$Y!O8WjZ zaUrc-BATPGRElNUDnZpUGyh^5Py{`k>kj*7|KuAVj}V4(8Gb- z68umji|dJKUPZC~-Zy?^5$v}-Kk$#i^(E8yF!8Ne;MIxs0v3KrAiNX)F()J>JPqXD z5}pp4NKu@Pi_xMu4`>FEi!ZRjKSp^`8+f~OgU?WTg7Tv9J+|k0=Rpx0MUE!^Ht%DI zWo78QbN#Fx^6K_iA<>L0PB}v)l4*qkecb}R5pU5oe3b?UyI@<(-V*by8Ke*y!1tyM zR?5NDwsV*-_{bStJyrW#_`{vu#YxrXraz+xuEXp~ool^RKKQ_SGyY}!5Ybb-o8{L_ zU&GX>%1*X&#>oa8LT~!^Eq$p?+GE8W$yTHYSPpygPt)%>e$$d5f=1+$TdT5&I;_~a zSLBFV#xvdQB3n{?JTJjVL=>s-&=OO~a!4m>4gfKK0mShJcPEc%Xa#?vS;?hl((^0% z+GTSGQ?Mz2aPcSdr*1R~iC`e^um_ee`R?!vpU0*B3M+J)?XT2FO<9T|bDwz61n=Ak z-fOW7DG6#6s1?#=6r3~K=bTY=dK8>93eFh?=Zu1LM!`9w;G9u#&L}u%6r3{(&KU*g zj0(;f1?+x+Mqgc}lMgRdlD{55#}G##jF@t?}IwdG#7! z_26LmHEVo>(kQvKM2FKWXA0@T{iD$0(O~ZgngrS9`B1)PjN~ZxIu8ufFojNy*u2CEgc0jMd5T`oVSyY5pv?N84b|eq5hdh`jTsRm6#N@MML3#^BG(3gXV4fz z(0_OnKJyT$Z_N*s@=KzLN&=tYBG*J{V6u>2Jt!@ynPV}3I*{8~f+0e7;EViPDmQOitKEOq*X~?@z*FyMB?r=J$m`39Nly>gHaaY$J|NZo|cUM|t z$y%+AS8EA9u&vcv2y97CY_I_bGf*dB8cZ+-89zjXk&VT}5tOZ`Tr2Cd0Irb@uB1?(f`l&OP__ zy?5``3|a|_@s{~ExMy9R5f^=+ee3o@ZH};EE0_&Ox9(+Fl#{vm(uyrxmuFtR`YRjV zjB4FsniVS8e4~3s@|g{*8^3n#53gk(KmC`R=YgHSrH56O9iS-S3H6yX=_^?59LKi6%f`ZJnT_aSWMows8ftI$aMcHk7%i@0+@h|H&15KH8&v*-D$S4}L|g{9<%?3DZ{} zuD(#aRvCFX^KdJAbn*4O9&5SK1qGrR`zkh6G~RsESNMqChm$Nkf5)8Y+7kDZ_Wy2o z{~~$seZRjixqi=yJ;`;qzjZr#7Lf6SqISGspS z^5m-TWQAZ7GEx3&OLo9dFJ(8->92)>d-m!U5yvuyO4c^&$xeJZ1hdm;8QH+ zCmKhkQ5S{1RUfaL_xAOuAE``uu!~N^RQW=O|lha5-otSfSU&?IB4YUc^> zY-cUzR-$EkwX6bd)9Zu^r+ZX|y)${WZM4FvwALj?Y}%R{qg3+uvIM_s&((fcU@zK2 zYjM)MAfoibTGx>Il~pyL`Fv{dQpM*yK9{oIQLd3kYU`}l{tbk;dTV1Y>aBK5;CKtl zxW(YZyE0bPY9H<5@{GQ|zj`!!Ma6a7YOZS3X3J{#zybHJj`~Y%H*HSd%F3DzHCxLH zzjfogAFW@nbB8`k*4J#nK5P|sZ*hOmr*___PXeWzv$m3B6xgLYO1SNl<ZoKxML`o{DuppdY}dOkkWdlA3~4>7v&W&`tVoHuJ0U@L#@;!p2HU=7X;cAsTw&wb)Yt)2?Ie5HbbtG02e6IS^I9GRFy3BpRjvT*rn}6v_A^R(rCU;&@ zABfblB`jNOVD9Uzf#uRtFX!tQo19Az;T77{eTc0E7ue?-XRUoM;_r>-FLDWe_i+t+ zd_%3Xn(H_|az5WwP;X=_T(nnTV*Tn%BynHP+daCAOOwCk^40%ZqRL}|itmH{e^#g{ zV(b-jb~ERE5+o^tnO33gbvn+_8$YF|a;q2zsA33as!s5WaTH#Q#kBfxxk~&J#$GaV zK==!v7w}CxeJ)D-=*;5aMd{5b!e^KZYGH7tmJl)Utku6SO6KORShcDy8d?xtw6G{r zQ64F&ysSJjv1oT>!lPQNJ7?x`S+t^PLuHu^boSqRZb|x64ldf`9B_T^W;(kE-J=;!Mj)d& zBg%=^?HOk>&&zx?^JwNo<{!8!HIP-DwK?mltk>8N@o@GTZZbZQbA}623v3HXYpSy{xGsUa!F)&u~MgWPw9cukIQPy_LaSGUgvo)mS0(MZpH4(<&_62M=GbP zN~`u%JzjOJ>e%@=EsZj7S-$jpOJ7*Fe%bZQZe8}svbUCBv;5c9@#@!NO|e6bMZ`0(^+ zgv&|qXTQ2q#*NlZPZF-T_y%%eeV1;@@2n<10JoD+aSJ#>I0TFv<0Ntq7`Fi2rU=I^ zeeLu(DxTT5lX0j5eZ6KJ{Ji%5LP17VNg2 zeghgHNBCJx5DReQpM_<~Ao3_CVrfb&zSP2H_OzTFgY-ndg~qUI(5bOBaqbHt4MtZ_|Bm=I#G}7aKk18*WD``D=gvWkDJCyKLW@!EMcmq3qeJUYJZ~)qf)*(tz>DNGu zDM24JMEbCW!xoOfe}c9-VpO&*F)u2i{Iv@zglxJsQDNDt1S^e0+%w2nZF?WuH+!*d*3f+gdW zMi9!$_)ytDjtvyAQivm+;*kYfYkAe9Q5?yY?`q1o);z5i$@dWsA#E-Er=aDesWq>t zMREaM^2|D9^4U%hQI$v?`KrB$k)ytJYzH`#vL4Rk&;XuOk4+R4v>4UOddsDrvD_qK zwFdQIq)JmxUiH+19 zcSReKt--Wtw6_|qel?m6Ra*31T+K(?kFW@jpIwc1@$L? z3JoORgytI!CPxSt+0&546dMg2jo8x?qfv_~wUlKRE>AwiyVc2ept0o7p*0pCPyU2( zt);9pTF=)6);JBxUqTy`A3#@I{2HU1lkalQ(>h9r8&vWCb(~V;gEk@gI;YuU+OY6C zZUxPT9<-QFd)kGj>-c`6;(M^eI;YoC_8EtMiyyG?ki9i*=|?QhsHGpXGzr3+csmG{ z47$nH2W{f%CT?^UZR6cd$e{QRi|Mqtx{zcOGAO>s!oBuXGHl|+U%jh-ag#G>;UPF>RKnowv4d6IC{}3;DM= z-9~$i_8RR&{w+?wg$JfTAv|O;l4J|WN;cZ6O>4pqqMMMvi6;s-QD2(SUEvODXcKqa z2BBR>yDg^2!o8NV&tm#5{eaOyiy1PWI`yRqm`*%LjE-5F1mR{Yg=X>%5^lGcPGoL2 zy_&JJ;-yzJD3A}@XEFUo2Q2*%)@bIY-E8O)OCt+6JF;-IGfLfTHVZdn;Ymi&k52!J zeTnO6QQOcu8@h#*ZRUa8s6xqbI!LywoAHOQWb~K%W%B$Ng-*z-rSemxuOG+s(YDX8*F^fsS z=b+_skX*uqrOQF`DugzX<{&wWD#wGor!)uQsk@3rJ1u>erIDQvvcf42y%z2>KK;zT z4tK}4wIn+85A$=I!wC`>PwLDfN@Zr z?I0JWk$-kzVTDyYI^kahm9KW1rk$o~C%NdUH0?AUJCRf|9TuZ{+KFDG^3_iA4MUZ0 zr{&v8JylrRb(;TlTE3l>Y&J1t7M~zZ7c#7buA>yXsGFi&@PsZrXB^sWVYM4wc+n)Z z6QAj#OcjjB^j172d>+C_U!$J=W^>zm9^=g2*7j6wU5~XVJm|o9R4r|w76RJym`0MTQCJh*9a=YclkirhS%gANl&= z(hQ$I%eRkw6;>VWBVUD;V;^se%Kvqbx5D!OKJ$}4{6r~L3;M`+3Oa(;eds;`mACX+ zJH!3s#PlOW1gg5(Z<6#QLm^?+%6?=}SRUJt3<^tzeq>OLWa#HdR9g3&*8L_!zsb;V zGW1(1_mg{=bd8L#1}xtJ%W=SLK43NUWmbkIC%(6k$*7EJL}ea9gFKMC!}CWF)hg;l!;jpq>3 zW;#RG-whd`A*)kE=9xoq8|SIG4VixqIUQJU2tS#Gc1@px%JYZdFM8Ohym5%Km;g_E zlrb3hz%#-;jLgH(>!yDMRm*>vyiP(@e-D$FXuHL9BH>{;E2ht4BtN&xPya@@%QMnD zg0%~wYJrbnZ6CCae2-w~Y^ePC2)0#NzI4Pqa~SIlqyIW;{jjwp!zRfvl1vaINrp|5 zVbgjTKH0>m#U8f)Vi>6=39D5ZM(c6tfW0+jVfD7d;HWY6j?zL-5i>^o82pz&)sl>voyXv>c(wdvX6-Sw zP*^q}LzhCRTB`Q4Wfi7GtKx7@t1uB`p%BMZ(G@VY(#9>m>2ATLN89 zK;?}IYatWnAqn%4gxM#7{Gahu@+WK@lAwkvEM4?(zVnvwBL2C6orWDjGmtYU70-D; z%)7iaSvCXp!r5q1lZxloU^a|<@!EZMMJnuP74w^^aDecg)Gjw`E-?YxXD^(AzTZ!U zGnsMyFcr=s-(RJ|*|c3BrNTMv&*P*R<1^QZxaFy^-&y6V_KJUi@V(ob_HS?5cjc}f zJNMPb;_LUAMPH<8e#vJ*u`x7q>LEZr`>y zx-k~r)zY+GztuZhx9!=v>z2K-rtSN8ZEM-Lt#x1QmfOGm$DVO8w2p<}?zC{B{FSUD z@8BwVO(|oj9cNpg|67O-@8_>=rjI1sN9--+ z{_X$sd**u(*1?`f_%HhRFE}ZHGbKIj{F8ID^LJ$VG&A}NzW3S03QZMazeZgeb$;t+ zvTm`IT|3LERljgvcYaBaBz!{HzzX&f*3#}LoPS2SH}J=2!0PMRe{zxYF5{~YSOs{K zL;ZF-$Cy)VX1v<@FXyN1cYlwwWgTZdN-{g3Q2L$StmwAzWuk3-?Wzp*FJ-Q|)w$8R ziCVGN`FH1i+JeiRw=vB@=Q!JHg3foHf8?v34^aRAoc~n(#8MMu^t`3bq}rqXl}AGqyH5|H%s2m6&}$UDF5Dhh1z(n z^N@3b-Il*$mt};7qXo z@jd5T&Phh=bKLCgSj(Na-IjIfovpX$-r2G%9=|y5ZdIAO{Q4B299^Wmi_i2w?^%R0 zb8p+WuZ3MAS$f1;sh+sa+glaq@!((rd(+iklDr&C*@#?EbM|kY1PCVzHoHuhzS(msucV})( z?sIeI&$(vK({oPGt(|-S++)7wzFOb?zE^!`{9pBN@bC8@@IT=Hk^kess=z&gm$0cY zJRx#I*oC4ALyHk7WL>zrP<3IA!iYH01w=i-(|mqI#0Yy8jxIDtSd0*K;paln)wc^d z2j7Oj-V%1}y#>Z+Z-IJ|LP)uAPNC$&$h|i;uMs9CbX*cDPvK`m&xDB!5f>gV)J#}- zl)r^n3I!JeDWps|rOw(z8RDemKN-;}bzIN0T+*-l)ASqrfiUM;j* zShbMqwAER2ARnQfLa05YQ;4OsdY+WvW1!N)q=iTekIt}C6&gJg%7lwbc%~#(CWYvT z1bj*-q0hpeg*3)&u;&Y}@Wvpmj;vEkPQrqP1dE?gV7!obgaOMtg#QZt753|S zF9$A?QkbvyY_n2?cYfYmBY0~#C9k}-Fy*abvxSgdd8<%eVY)(eh35**6_(3Yzi2Kb zS2(UvTw%DL&4-b20@+nlJPQhS&xe19=rs<1#-QX!De<VXZ=1g|iA}6~-!rRrspVRbi__R)wnyRTZWxL{)gI&{ScmLQ;jJ3Plx$ zDg@Q=Q{sf37TFkBsHJ8bLQRF43Nh7;L$i0y-G!72QPM0y7^%j=8U+hY(g;{HX5pfm zBWe6DWGwu7&cq6_Y9pnG+kl3UN@!I#Tr@w>yk>@TKD~N2+%==pe22LTVWDtdEkH!%ViQExFk=j%2b!6Ou|fnZM36Pi>`?Q&Fe92mMlPB!L>QM8(u-=IAxu*9bD=CA z#uD^o;w|A3-dwp5nI=;*`DST+9B$HhlKxYOV?H{+VKZgTIaO{#8fSEzLdTOf$C8f1 z7KJRTY+Se9CGn*(&5y$)@EK4lE=zEt_N#3MG!|l9je$N;U{b$whhaD;!U_ zj4(XeZzUxGhR?6&slx6w-!?y|w1nBI)@Wuev{zW2kUHUXLg|Fj1(WlK1^O` z{e`zxON7SBM}$nN1PW4bvt9rOH&cTrX4PJhJ5pO6<5P9ayaL&Tw0ZSOWjRHAEsV|c zSCwPh+Cqxu6+%!!Kj9*K%~;=utqQS~EHA%Rd%zq$CF24(s!pXn1Eib0gqX?CWQMj7*33IO)f2nrOl&sJ&&+CL+%j*hLywjd0{K=DaHg8YT zu$5@FarC71^!FtFJhyaxP5Xye$2_0Squ2eJ*D9f3x`vuu=%Kbm_?6HfVOMFdnn%8w zsdh|tTArk~c-+tdwZ+1zyjJs+m8S41p;IbBAyXNKObM6rll!otQeN&SXL;pGBv6YB z!iWb*3fC|w*D$ELct9bNgefo8vq{QHW#pB%%0^yCy9<9IPM+sW-icXxO!0>BZm$(d z*8m|)!j(K!Np;AF#npNXPx3fSk|%uxzw!-9Dg?0SV-u3^VJ>+$^F^%+bzsFVY5{mV@_(QDB{X)38S7zYpf97t LFm?nPa}xgpDq>tJ literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/calibri.ttf b/vendor/szymach/c-pchart/resources/fonts/calibri.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8b6e3c9743151d253ef32fe27e386ed6707312cd GIT binary patch literal 811052 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�T#SJf@* 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ෳ-FYGPZ>(~o*P`QhbM6_ga9XN2qAT85q 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 Wa%z020eM!EKP7!_1CK~}Codes = []; + $this->Reverse = []; + if (!file_exists($filePath)) { + $filePath = sprintf('%s/../../resources/barcode/128B.db', __DIR__); + } + + $FileHandle = @fopen($filePath, "r"); + if (!$FileHandle) { + throw new Exception( + sprintf("Cannot find barcode database (%s).", $filePath) + ); + } + + while (!feof($FileHandle)) { + $Buffer = fgets($FileHandle, 4096); + $Buffer = str_replace(chr(10), "", $Buffer); + $Buffer = str_replace(chr(13), "", $Buffer); + $Values = preg_split("/;/", $Buffer); + + $this->Codes[$Values[1]]["ID"] = $Values[0]; + $this->Codes[$Values[1]]["Code"] = $Values[2]; + $this->Reverse[$Values[0]]["Code"] = $Values[2]; + $this->Reverse[$Values[0]]["Asc"] = $Values[1]; + } + fclose($FileHandle); + } + + /** + * Return the projected size of a barcode + * + * @param string $TextString + * @param string $Format + * @return array + */ + public function getSize($TextString, $Format = "") + { + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : false; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : false; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + + $TextString = $this->encode128($TextString); + $BarcodeLength = strlen($this->Result); + + $WOffset = $DrawArea ? 20 : 0; + $HOffset = $ShowLegend ? $FontSize + $LegendOffset + $WOffset : 0; + + $X1 = cos($Angle * PI / 180) * ($WOffset + $BarcodeLength); + $Y1 = sin($Angle * PI / 180) * ($WOffset + $BarcodeLength); + + $X2 = $X1 + cos(($Angle + 90) * PI / 180) * ($HOffset + $Height); + $Y2 = $Y1 + sin(($Angle + 90) * PI / 180) * ($HOffset + $Height); + + + $AreaWidth = max(abs($X1), abs($X2)); + $AreaHeight = max(abs($Y1), abs($Y2)); + + return ["Width" => $AreaWidth, "Height" => $AreaHeight]; + } + + /** + * + * @param string $Value + * @param string $Format + * @return string + */ + public function encode128($Value, $Format = "") + { + $this->Result = "11010010000"; + $this->CRC = 104; + $TextString = ""; + + for ($i = 1; $i <= strlen($Value); $i++) { + $CharCode = ord($this->mid($Value, $i, 1)); + if (isset($this->Codes[$CharCode])) { + $this->Result = $this->Result . $this->Codes[$CharCode]["Code"]; + $this->CRC = $this->CRC + $i * $this->Codes[$CharCode]["ID"]; + $TextString = $TextString . chr($CharCode); + } + } + $this->CRC = $this->CRC - floor($this->CRC / 103) * 103; + + $this->Result = $this->Result . $this->Reverse[$this->CRC]["Code"]; + $this->Result = $this->Result . "1100011101011"; + + return $TextString; + } + + /** + * Create the encoded string + * @param Image $Object + * @param string $Value + * @param int $X + * @param int $Y + * @param array $Format + */ + public function draw(Image $Object, $Value, $X, $Y, $Format = []) + { + $this->pChartObject = $Object; + + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : false; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : false; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; + $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; + $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; + $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; + + $TextString = $this->encode128($Value); + + if ($DrawArea) { + $X1 = $X + cos(($Angle - 135) * PI / 180) * 10; + $Y1 = $Y + sin(($Angle - 135) * PI / 180) * 10; + + $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result) + 20); + $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result) + 20); + + if ($ShowLegend) { + $X3 = $X2 + + cos(($Angle + 90) * PI / 180) + * ($Height + $LegendOffset + $this->pChartObject->FontSize + 10) + ; + $Y3 = $Y2 + + sin(($Angle + 90) * PI / 180) + * ($Height + $LegendOffset + $this->pChartObject->FontSize + 10) + ; + } else { + $X3 = $X2 + cos(($Angle + 90) * PI / 180) * ($Height + 20); + $Y3 = $Y2 + sin(($Angle + 90) * PI / 180) * ($Height + 20); + } + + $X4 = $X3 + cos(($Angle + 180) * PI / 180) * (strlen($this->Result) + 20); + $Y4 = $Y3 + sin(($Angle + 180) * PI / 180) * (strlen($this->Result) + 20); + + $Polygon = [$X1, $Y1, $X2, $Y2, $X3, $Y3, $X4, $Y4]; + $Settings = [ + "R" => $AreaR, + "G" => $AreaG, + "B" => $AreaB, + "BorderR" => $AreaBorderR, + "BorderG" => $AreaBorderG, + "BorderB" => $AreaBorderB + ]; + $this->pChartObject->drawPolygon($Polygon, $Settings); + } + + for ($i = 1; $i <= strlen($this->Result); $i++) { + if ($this->mid($this->Result, $i, 1) == 1) { + $X1 = $X + cos($Angle * PI / 180) * $i; + $Y1 = $Y + sin($Angle * PI / 180) * $i; + $X2 = $X1 + cos(($Angle + 90) * PI / 180) * $Height; + $Y2 = $Y1 + sin(($Angle + 90) * PI / 180) * $Height; + + $Settings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + $this->pChartObject->drawLine($X1, $Y1, $X2, $Y2, $Settings); + } + } + + if ($ShowLegend) { + $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result) / 2); + $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result) / 2); + + $LegendX = $X1 + cos(($Angle + 90) * PI / 180) * ($Height + $LegendOffset); + $LegendY = $Y1 + sin(($Angle + 90) * PI / 180) * ($Height + $LegendOffset); + + $Settings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Angle" => -$Angle, + "Align" => TEXT_ALIGN_TOPMIDDLE + ]; + $this->pChartObject->drawText($LegendX, $LegendY, $TextString, $Settings); + } + } + + /** + * + * @param string $value + * @param int $NbChar + * @return string + */ + public function left($value, $NbChar) + { + return substr($value, 0, $NbChar); + } + + /** + * + * @param string $value + * @param int $NbChar + * @return string + */ + public function right($value, $NbChar) + { + return substr($value, strlen($value) - $NbChar, $NbChar); + } + + /** + * + * @param string $value + * @param int $Depart + * @param int $NbChar + * @return string + */ + public function mid($value, $Depart, $NbChar) + { + return substr($value, $Depart - 1, $NbChar); + } +} diff --git a/vendor/szymach/c-pchart/src/Barcode/Barcode39.php b/vendor/szymach/c-pchart/src/Barcode/Barcode39.php new file mode 100644 index 0000000..50f003a --- /dev/null +++ b/vendor/szymach/c-pchart/src/Barcode/Barcode39.php @@ -0,0 +1,296 @@ +MOD43 = (boolean) $EnableMOD43; + $this->Codes = []; + $this->Reverse = []; + if (!file_exists($filePath)) { + $filePath = sprintf('%s/../../resources/barcode/39.db', __DIR__); + } + + $FileHandle = @fopen($filePath, "r"); + if (!$FileHandle) { + throw new Exception( + "Cannot find barcode database (" . $filePath . ")." + ); + } + + while (!feof($FileHandle)) { + $Buffer = fgets($FileHandle, 4096); + $Buffer = str_replace(chr(10), "", $Buffer); + $Buffer = str_replace(chr(13), "", $Buffer); + $Values = preg_split("/;/", $Buffer); + + $this->Codes[$Values[0]] = $Values[1]; + } + fclose($FileHandle); + } + + /** + * Return the projected size of a barcode + * + * @param string $TextString + * @param string $Format + * @return array + */ + public function getSize($TextString, $Format = "") + { + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : false; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : false; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + + $TextString = $this->encode39($TextString); + $BarcodeLength = strlen($this->Result); + + $WOffset = $DrawArea ? 20 : 0; + $HOffset = $ShowLegend ? $FontSize + $LegendOffset + $WOffset : 0; + + $X1 = cos($Angle * PI / 180) * ($WOffset + $BarcodeLength); + $Y1 = sin($Angle * PI / 180) * ($WOffset + $BarcodeLength); + + $X2 = $X1 + cos(($Angle + 90) * PI / 180) * ($HOffset + $Height); + $Y2 = $Y1 + sin(($Angle + 90) * PI / 180) * ($HOffset + $Height); + + + $AreaWidth = max(abs($X1), abs($X2)); + $AreaHeight = max(abs($Y1), abs($Y2)); + + return ["Width" => $AreaWidth, "Height" => $AreaHeight]; + } + + /** + * Create the encoded string + * + * @param string $Value + * @return string + */ + public function encode39($Value) + { + $this->Result = "100101101101" . "0"; + $TextString = ""; + for ($i = 1; $i <= strlen($Value); $i++) { + $CharCode = ord($this->mid($Value, $i, 1)); + if ($CharCode >= 97 && $CharCode <= 122) { + $CharCode = $CharCode - 32; + } + + if (isset($this->Codes[chr($CharCode)])) { + $this->Result = $this->Result . $this->Codes[chr($CharCode)] . "0"; + $TextString = $TextString . chr($CharCode); + } + } + + if ($this->MOD43) { + $Checksum = $this->checksum($TextString); + $this->Result = $this->Result . $this->Codes[$Checksum] . "0"; + } + + $this->Result = $this->Result . "100101101101"; + $TextString = "*" . $TextString . "*"; + + return $TextString; + } + + /** + * Create the encoded string + * @param Image $Object + * @param type $Value + * @param type $X + * @param type $Y + * @param array $Format + */ + public function draw(Image $Object, $Value, $X, $Y, $Format = []) + { + $this->pChartObject = $Object; + + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : false; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : false; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; + $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; + $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; + $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; + + $TextString = $this->encode39($Value); + + if ($DrawArea) { + $X1 = $X + cos(($Angle - 135) * PI / 180) * 10; + $Y1 = $Y + sin(($Angle - 135) * PI / 180) * 10; + + $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result) + 20); + $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result) + 20); + + if ($ShowLegend) { + $X3 = $X2 + + cos(($Angle + 90) * PI / 180) + * ($Height + $LegendOffset + $this->pChartObject->FontSize + 10) + ; + $Y3 = $Y2 + + sin(($Angle + 90) * PI / 180) + * ($Height + $LegendOffset + $this->pChartObject->FontSize + 10) + ; + } else { + $X3 = $X2 + cos(($Angle + 90) * PI / 180) * ($Height + 20); + $Y3 = $Y2 + sin(($Angle + 90) * PI / 180) * ($Height + 20); + } + + $X4 = $X3 + cos(($Angle + 180) * PI / 180) * (strlen($this->Result) + 20); + $Y4 = $Y3 + sin(($Angle + 180) * PI / 180) * (strlen($this->Result) + 20); + + $Polygon = [$X1, $Y1, $X2, $Y2, $X3, $Y3, $X4, $Y4]; + $Settings = [ + "R" => $AreaR, + "G" => $AreaG, + "B" => $AreaB, + "BorderR" => $AreaBorderR, + "BorderG" => $AreaBorderG, + "BorderB" => $AreaBorderB + ]; + $this->pChartObject->drawPolygon($Polygon, $Settings); + } + + for ($i = 1; $i <= strlen($this->Result); $i++) { + if ($this->mid($this->Result, $i, 1) == 1) { + $X1 = $X + cos($Angle * PI / 180) * $i; + $Y1 = $Y + sin($Angle * PI / 180) * $i; + $X2 = $X1 + cos(($Angle + 90) * PI / 180) * $Height; + $Y2 = $Y1 + sin(($Angle + 90) * PI / 180) * $Height; + + $Settings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + $this->pChartObject->drawLine($X1, $Y1, $X2, $Y2, $Settings); + } + } + + if ($ShowLegend) { + $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result) / 2); + $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result) / 2); + + $LegendX = $X1 + cos(($Angle + 90) * PI / 180) * ($Height + $LegendOffset); + $LegendY = $Y1 + sin(($Angle + 90) * PI / 180) * ($Height + $LegendOffset); + + $Settings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Angle" => -$Angle, + "Align" => TEXT_ALIGN_TOPMIDDLE + ]; + $this->pChartObject->drawText($LegendX, $LegendY, $TextString, $Settings); + } + } + + /** + * @param string $string + * @return string + */ + public function checksum($string) + { + $checksum = 0; + $length = strlen($string); + $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%'; + + for ($i = 0; $i < $length; ++$i) { + $checksum += strpos($charset, $string[$i]); + } + return substr($charset, ($checksum % 43), 1); + } + + /** + * @param string $value + * @param int $NbChar + * @return string + */ + public function left($value, $NbChar) + { + return substr($value, 0, $NbChar); + } + + /** + * @param string $value + * @param int $NbChar + * @return string|false + */ + public function right($value, $NbChar) + { + return substr($value, strlen($value) - $NbChar, $NbChar); + } + + /** + * @param string $value + * @param int $Depart + * @param int $NbChar + * @return string + */ + public function mid($value, $Depart, $NbChar) + { + return substr($value, $Depart - 1, $NbChar); + } +} diff --git a/vendor/szymach/c-pchart/src/BaseDraw.php b/vendor/szymach/c-pchart/src/BaseDraw.php new file mode 100644 index 0000000..8206711 --- /dev/null +++ b/vendor/szymach/c-pchart/src/BaseDraw.php @@ -0,0 +1,1728 @@ +resourcePath = sprintf('%s/../resources', __DIR__); + $this->FontName = $this->loadFont($this->FontName, 'fonts'); + } + + /** + * Set the path to the folder containing library resources (fonts, data, palettes). + * + * @param string $path + * @throws Exception + */ + public function setResourcePath($path) + { + $escapedPath = rtrim($path, '/'); + if (!file_exists($escapedPath)) { + throw new Exception(sprintf( + "The path '%s' to resources' folder does not exist!", + $escapedPath + )); + } + + $this->resourcePath = $escapedPath; + } + + /** + * Check if requested resource exists and return the path to it if yes. + * @param string $name + * @param string $type + * @return string + * @throws Exception + */ + protected function loadFont($name, $type) + { + if (file_exists($name)) { + return $name; + } + + $path = sprintf('%s/%s/%s', $this->resourcePath, $type, $name); + if (file_exists($path)) { + return $path; + } + + throw new Exception( + sprintf('The requested resource %s (%s) has not been found!', $name, $type) + ); + } + + /** + * Allocate a color with transparency + * @param resource $Picture + * @param int $R + * @param int $G + * @param int $B + * @param int $Alpha + * @return int + */ + public function allocateColor($Picture, $R, $G, $B, $Alpha = 100) + { + if ($R < 0) { + $R = 0; + } + if ($R > 255) { + $R = 255; + } + if ($G < 0) { + $G = 0; + } + if ($G > 255) { + $G = 255; + } + if ($B < 0) { + $B = 0; + } + if ($B > 255) { + $B = 255; + } + if ($Alpha < 0) { + $Alpha = 0; + } + if ($Alpha > 100) { + $Alpha = 100; + } + + $Alpha = $this->convertAlpha($Alpha); + return imagecolorallocatealpha($Picture, (int) $R, (int) $G, (int) $B, (int) $Alpha); + } + + /** + * Convert apha to base 10 + * @param int|float $AlphaValue + * @return integer + */ + public function convertAlpha($AlphaValue) + { + return floor((127 / 100) * (100 - $AlphaValue)); + } + + /** + * @param string $FileName + * @return array + */ + public function getPicInfo($FileName) + { + $Infos = getimagesize($FileName); + $Width = $Infos[0]; + $Height = $Infos[1]; + $Type = $Infos["mime"]; + + if ($Type == "image/png") { + $Type = 1; + } + if ($Type == "image/gif") { + $Type = 2; + } + if ($Type == "image/jpeg ") { + $Type = 3; + } + + return [$Width, $Height, $Type]; + } + + /** + * Compute the scale, check for the best visual factors + * @param int $XMin + * @param int $XMax + * @param int $MaxDivs + * @param array $Factors + * @param int $AxisID + * @return mixed + */ + public function computeScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID = 0) + { + /* Compute each factors */ + $Results = []; + foreach ($Factors as $Key => $Factor) { + $Results[$Factor] = $this->processScale($XMin, $XMax, $MaxDivs, [$Factor], $AxisID); + } + /* Remove scales that are creating to much decimals */ + $GoodScaleFactors = []; + foreach ($Results as $Key => $Result) { + $Decimals = preg_split("/\./", $Result["RowHeight"]); + if ((!isset($Decimals[1])) || (strlen($Decimals[1]) < 6)) { + $GoodScaleFactors[] = $Key; + } + } + + /* Found no correct scale, shame,... returns the 1st one as default */ + if (!count($GoodScaleFactors)) { + return $Results[$Factors[0]]; + } + + /* Find the factor that cause the maximum number of Rows */ + $MaxRows = 0; + $BestFactor = 0; + foreach ($GoodScaleFactors as $Key => $Factor) { + if ($Results[$Factor]["Rows"] > $MaxRows) { + $MaxRows = $Results[$Factor]["Rows"]; + $BestFactor = $Factor; + } + } + + /* Return the best visual scale */ + return $Results[$BestFactor]; + } + + /** + * Compute the best matching scale based on size & factors + * @param int $XMin + * @param int $XMax + * @param int $MaxDivs + * @param array $Factors + * @param int $AxisID + * @return array + */ + public function processScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID) + { + $ScaleHeight = abs(ceil($XMax) - floor($XMin)); + + $Format = null; + if (isset($this->DataSet->Data["Axis"][$AxisID]["Format"])) { + $Format = $this->DataSet->Data["Axis"][$AxisID]["Format"]; + } + + $Mode = AXIS_FORMAT_DEFAULT; + if (isset($this->DataSet->Data["Axis"][$AxisID]["Display"])) { + $Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"]; + } + + $Scale = []; + if ($XMin != $XMax) { + $Found = false; + $Rescaled = false; + $Scaled10Factor = .0001; + $Result = 0; + while (!$Found) { + foreach ($Factors as $Key => $Factor) { + if (!$Found) { + $XMinRescaled = $XMin; + if (!($this->modulo($XMin, $Factor * $Scaled10Factor) == 0) + || ($XMin != floor($XMin)) + ) { + $XMinRescaled = floor($XMin / ($Factor * $Scaled10Factor)) + * $Factor + * $Scaled10Factor + ; + } + + $XMaxRescaled = $XMax; + if (!($this->modulo($XMax, $Factor * $Scaled10Factor) == 0) + || ($XMax != floor($XMax)) + ) { + $XMaxRescaled = floor($XMax / ($Factor * $Scaled10Factor)) + * $Factor + * $Scaled10Factor + + ($Factor * $Scaled10Factor) + ; + } + + $ScaleHeightRescaled = abs($XMaxRescaled - $XMinRescaled); + + if (!$Found + && floor($ScaleHeightRescaled / ($Factor * $Scaled10Factor)) <= $MaxDivs + ) { + $Found = true; + $Rescaled = true; + $Result = $Factor * $Scaled10Factor; + } + } + } + $Scaled10Factor = $Scaled10Factor * 10; + } + + /* ReCall Min / Max / Height */ + if ($Rescaled) { + $XMin = $XMinRescaled; + $XMax = $XMaxRescaled; + $ScaleHeight = $ScaleHeightRescaled; + } + + /* Compute rows size */ + $Rows = floor($ScaleHeight / $Result); + if ($Rows == 0) { + $Rows = 1; + } + $RowHeight = $ScaleHeight / $Rows; + + /* Return the results */ + $Scale["Rows"] = $Rows; + $Scale["RowHeight"] = $RowHeight; + $Scale["XMin"] = $XMin; + $Scale["XMax"] = $XMax; + + /* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */ + if ($Mode == AXIS_FORMAT_METRIC && $Format == null) { + $Done = false; + $GoodDecimals = 0; + for ($Decimals = 0; $Decimals <= 10; $Decimals++) { + if (!$Done) { + $LastLabel = "zob"; + $ScaleOK = true; + for ($i = 0; $i <= $Rows; $i++) { + $Value = $XMin + $i * $RowHeight; + $Label = $this->scaleFormat($Value, AXIS_FORMAT_METRIC, $Decimals); + + if ($LastLabel == $Label) { + $ScaleOK = false; + } + $LastLabel = $Label; + } + if ($ScaleOK) { + $Done = true; + $GoodDecimals = $Decimals; + } + } + } + $Scale["Format"] = $GoodDecimals; + } + } else { + /* If all values are the same we keep a +1/-1 scale */ + $Rows = 2; + $XMin = $XMax - 1; + $XMax = $XMax + 1; + $RowHeight = 1; + + /* Return the results */ + $Scale["Rows"] = $Rows; + $Scale["RowHeight"] = $RowHeight; + $Scale["XMin"] = $XMin; + $Scale["XMax"] = $XMax; + } + + return $Scale; + } + + /** + * + * @param int|float $Value1 + * @param int|float $Value2 + * @return double + */ + public function modulo($Value1, $Value2) + { + if (floor($Value2) == 0) { + return 0; + } + if (floor($Value2) != 0) { + return (int) $Value1 % (int) $Value2; + } + + $MinValue = min($Value1, $Value2); + $Factor = 10; + while (floor($MinValue * $Factor) == 0) { + $Factor = $Factor * 10; + } + + return floor($Value1 * $Factor) % floor($Value2 * $Factor); + } + + /** + * @param mixed $Value + * @param mixed $LastValue + * @param integer $LabelingMethod + * @param integer $ID + * @param boolean $LabelSkip + * @return boolean + */ + public function isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) + { + if ($LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue) { + return true; + } + if ($LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue) { + return false; + } + if ($LabelingMethod == LABELING_ALL && $LabelSkip == 0) { + return true; + } + if ($LabelingMethod == LABELING_ALL && ($ID + $LabelSkip) % ($LabelSkip + 1) != 1) { + return false; + } + + return true; + } + + /** + * Returns the number of drawable series + * @return int + */ + public function countDrawableSeries() + { + $count = 0; + $Data = $this->DataSet->getData(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $count++; + } + } + + return $count; + } + + /** + * Fix box coordinates + * @param int $Xa + * @param int $Ya + * @param int $Xb + * @param int $Yb + * @return integer[] + */ + public function fixBoxCoordinates($Xa, $Ya, $Xb, $Yb) + { + return [ + (int) min($Xa, $Xb), + (int) min($Ya, $Yb), + (int) max($Xa, $Xb), + (int) max($Ya, $Yb) + ]; + } + + /** + * Apply AALias correction to the rounded box boundaries + * @param int|float $Value + * @param int $Mode + * @return int|float + */ + public function offsetCorrection($Value, $Mode) + { + $Value = round($Value, 1); + + if ($Value == 0 && $Mode != 1) { + return 0; + } + + if ($Mode == 1) { + if ($Value == .5) { + return .5; + } + if ($Value == .8) { + return .6; + } + if (in_array($Value, [.4, .7])) { + return .7; + } + if (in_array($Value, [.2, .3, .6])) { + return .8; + } + if (in_array($Value, [0, 1, .1, .9])) { + return .9; + } + } + + if ($Mode == 2) { + if ($Value == .1) { + return .1; + } + if ($Value == .2) { + return .2; + } + if ($Value == .3) { + return .3; + } + if ($Value == .4) { + return .4; + } + if ($Value == .5) { + return .5; + } + if ($Value == .7) { + return .7; + } + if (in_array($Value, [.6, .8])) { + return .8; + } + if (in_array($Value, [1, .9])) { + return .9; + } + } + + if ($Mode == 3) { + if (in_array($Value, [1, .1])) { + return .1; + } + if ($Value == .2) { + return .2; + } + if ($Value == .3) { + return .3; + } + if (in_array($Value, [.4, .8])) { + return .4; + } + if ($Value == .5) { + return .9; + } + if ($Value == .6) { + return .6; + } + if ($Value == .7) { + return .7; + } + if ($Value == .9) { + return .5; + } + } + + if ($Mode == 4) { + if ($Value == 1) { + return -1; + } + if (in_array($Value, [.1, .4, .7, .8, .9])) { + return .1; + } + if ($Value == .2) { + return .2; + } + if ($Value == .3) { + return .3; + } + if ($Value == .5) { + return -.1; + } + if ($Value == .6) { + return .8; + } + } + } + + /** + * Get the legend box size + * @param array $Format + * @return array + */ + public function getLegendSize(array $Format = []) + { + $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + + $Data = $this->DataSet->getData(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true + && $SerieName != $Data["Abscissa"] + && isset($Serie["Picture"]) + ) { + list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]); + if ($IconAreaWidth < $PicWidth) { + $IconAreaWidth = $PicWidth; + } + if ($IconAreaHeight < $PicHeight) { + $IconAreaHeight = $PicHeight; + } + } + } + + $YStep = max($this->FontSize, $IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $X = 100; + $Y = 100; + + $Boundaries = []; + $Boundaries["L"] = $X; + $Boundaries["T"] = $Y; + $Boundaries["R"] = 0; + $Boundaries["B"] = 0; + $vY = $Y; + $vX = $X; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + if ($Mode == LEGEND_VERTICAL) { + $BoxArray = $this->getTextBox( + $vX + $IconAreaWidth + 4, + $vY + $IconAreaHeight / 2, + $FontName, + $FontSize, + 0, + $Serie["Description"] + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Lines = preg_split("/\n/", $Serie["Description"]); + $vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Serie["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->getTextBox( + $vX + $IconAreaWidth + 6, + $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key), + $FontName, + $FontSize, + 0, + $Value + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX = max($Width) + $XStep; + } + } + } + $vY = $vY - $YStep; + $vX = $vX - $XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) { + $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset; + } + + $Width = ($Boundaries["R"] + $Margin) - ($Boundaries["L"] - $Margin); + $Height = ($Boundaries["B"] + $Margin) - ($Boundaries["T"] - $Margin); + + return ["Width" => $Width, "Height" => $Height]; + } + + /** + * Return the abscissa margin + * @param array $Data + * @return int + */ + public function getAbscissaMargin(array $Data) + { + foreach ($Data["Axis"] as $Values) { + if ($Values["Identity"] == AXIS_X) { + return $Values["Margin"]; + } + } + return 0; + } + + /** + * Returns a random color + * @param int $Alpha + * @return array + */ + public function getRandomColor($Alpha = 100) + { + return [ + "R" => rand(0, 255), + "G" => rand(0, 255), + "B" => rand(0, 255), + "Alpha" => $Alpha + ]; + } + + /** + * Validate a palette + * @param mixed $Colors + * @param int|float $Surrounding + * @return array + */ + public function validatePalette($Colors, $Surrounding = null) + { + $Result = []; + + if (!is_array($Colors)) { + return $this->getRandomColor(); + } + + foreach ($Colors as $Key => $Values) { + if (isset($Values["R"])) { + $Result[$Key]["R"] = $Values["R"]; + } else { + $Result[$Key]["R"] = rand(0, 255); + } + + if (isset($Values["G"])) { + $Result[$Key]["G"] = $Values["G"]; + } else { + $Result[$Key]["G"] = rand(0, 255); + } + + if (isset($Values["B"])) { + $Result[$Key]["B"] = $Values["B"]; + } else { + $Result[$Key]["B"] = rand(0, 255); + } + if (isset($Values["Alpha"])) { + $Result[$Key]["Alpha"] = $Values["Alpha"]; + } else { + $Result[$Key]["Alpha"] = 100; + } + + if (null !== $Surrounding) { + $Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding; + $Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding; + $Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding; + } else { + if (isset($Values["BorderR"])) { + $Result[$Key]["BorderR"] = $Values["BorderR"]; + } else { + $Result[$Key]["BorderR"] = $Result[$Key]["R"]; + } + if (isset($Values["BorderG"])) { + $Result[$Key]["BorderG"] = $Values["BorderG"]; + } else { + $Result[$Key]["BorderG"] = $Result[$Key]["G"]; + } + if (isset($Values["BorderB"])) { + $Result[$Key]["BorderB"] = $Values["BorderB"]; + } else { + $Result[$Key]["BorderB"] = $Result[$Key]["B"]; + } + if (isset($Values["BorderAlpha"])) { + $Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"]; + } else { + $Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"]; + } + } + } + + return $Result; + } + + /** + * @param mixed $Values + * @param array $Option + * @param boolean $ReturnOnly0Height + * @return int|float|array + */ + public function scaleComputeY($Values, array $Option = [], $ReturnOnly0Height = false) + { + $AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0; + $SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : null; + + $Data = $this->DataSet->getData(); + if (!isset($Data["Axis"][$AxisID])) { + return -1; + } + + if ($SerieName != null) { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + } + if (!is_array($Values)) { + $tmp = $Values; + $Values = []; + $Values[0] = $tmp; + } + + $Result = []; + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"] * 2; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + if ($ReturnOnly0Height) { + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $Step * $Value; + } + } + } else { + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $this->GraphAreaY2 + - $Data["Axis"][$AxisID]["Margin"] + - ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) + ; + } + } + } + } else { + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"] * 2; + $ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Width / $ScaleWidth; + + if ($ReturnOnly0Height) { + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $Step * $Value; + } + } + } else { + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $this->GraphAreaX1 + + $Data["Axis"][$AxisID]["Margin"] + + ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) + ; + } + } + } + } + return count($Result) == 1 ? reset($Result) : $Result; + } + + /** + * Format the axis values + * @param mixed $Value + * @param int $Mode + * @param array $Format + * @param string $Unit + * @return string + */ + public function scaleFormat($Value, $Mode = null, $Format = null, $Unit = null) + { + if ($Value == VOID) { + return ""; + } + + if ($Mode == AXIS_FORMAT_TRAFFIC) { + if ($Value == 0) { + return "0B"; + } + $Units = ["B", "KB", "MB", "GB", "TB", "PB"]; + $Sign = ""; + if ($Value < 0) { + $Value = abs($Value); + $Sign = "-"; + } + + $Value = number_format($Value / pow(1024, ($Scale = floor(log($Value, 1024)))), 2, ",", "."); + return $Sign . $Value . " " . $Units[$Scale]; + } + + if ($Mode == AXIS_FORMAT_CUSTOM) { + if (is_callable($Format)) { + return call_user_func($Format, $Value); + } + } + + if ($Mode == AXIS_FORMAT_DATE) { + $Pattern = "d/m/Y"; + if ($Format !== null) { + $Pattern = $Format; + } + + return gmdate($Pattern, $Value); + } + + if ($Mode == AXIS_FORMAT_TIME) { + $Pattern = "H:i:s"; + if ($Format !== null) { + $Pattern = $Format; + } + + return gmdate($Pattern, $Value); + } + + if ($Mode == AXIS_FORMAT_CURRENCY) { + return $Format . number_format($Value, 2); + } + + if ($Mode == AXIS_FORMAT_METRIC) { + if (abs($Value) > 1000000000) { + return round($Value / 1000000000, $Format) . "g" . $Unit; + } + if (abs($Value) > 1000000) { + return round($Value / 1000000, $Format) . "m" . $Unit; + } elseif (abs($Value) >= 1000) { + return round($Value / 1000, $Format) . "k" . $Unit; + } + } + return $Value . $Unit; + } + + /** + * @return array|null + */ + public function scaleGetXSettings() + { + $Data = $this->DataSet->getData(); + foreach ($Data["Axis"] as $Settings) { + if ($Settings["Identity"] == AXIS_X) { + return [$Settings["Margin"], $Settings["Rows"]]; + } + } + } + + /** + * Write Max value on a chart + * @param int $Type + * @param array $Format + */ + public function writeBounds($Type = BOUND_BOTH, $Format = null) + { + $MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max="; + $MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min="; + $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1; + $ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : ""; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0; + $MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0; + $MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0; + $MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255; + $MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255; + $MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255; + $MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO; + $MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + + $CaptionSettings = [ + "DrawBox" => $DrawBox, + "DrawBoxBorder" => $DrawBoxBorder, + "BorderOffset" => $BorderOffset, + "BoxRounded" => $BoxRounded, + "RoundedRadius" => $RoundedRadius, + "BoxR" => $BoxR, + "BoxG" => $BoxG, + "BoxB" => $BoxB, + "BoxAlpha" => $BoxAlpha, + "BoxSurrounding" => $BoxSurrounding, + "BoxBorderR" => $BoxBorderR, + "BoxBorderG" => $BoxBorderG, + "BoxBorderB" => $BoxBorderB, + "BoxBorderAlpha" => $BoxBorderAlpha + ]; + + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + $Data = $this->DataSet->getData(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true + && $SerieName != $Data["Abscissa"] + && !isset($ExcludedSeries[$SerieName]) + ) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + + $MinValue = $this->DataSet->getMin($SerieName); + $MaxValue = $this->DataSet->getMax($SerieName); + + $MinPos = VOID; + $MaxPos = VOID; + foreach ($Serie["Data"] as $Key => $Value) { + if ($Value == $MinValue && $MinPos == VOID) { + $MinPos = $Key; + } + if ($Value == $MaxValue) { + $MaxPos = $Key; + } + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + $X = $this->GraphAreaX1 + $XMargin; + $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; + + if ($Type == BOUND_MAX || $Type == BOUND_BOTH) { + if ($MaxLabelPos == BOUND_LABEL_POS_TOP + || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) + ) { + $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + } + if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM + || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) + ) { + $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; + $Align = TEXT_ALIGN_TOPMIDDLE; + } + + $XPos = $X + $MaxPos * $XStep + $SerieOffset; + $Label = sprintf( + '%s%s', + $MaxLabelTxt, + $this->scaleFormat(round($MaxValue, $Decimals), $Mode, $Format, $Unit) + ); + + $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label); + $XOffset = 0; + $YOffset = 0; + if ($TxtPos[0]["X"] < $this->GraphAreaX1) { + $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2); + } + if ($TxtPos[1]["X"] > $this->GraphAreaX2) { + $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2); + } + if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { + $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; + } + if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { + $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); + } + + $CaptionSettings["R"] = $MaxDisplayR; + $CaptionSettings["G"] = $MaxDisplayG; + $CaptionSettings["B"] = $MaxDisplayB; + $CaptionSettings["Align"] = $Align; + + $this->drawText($XPos + $XOffset, $YPos + $YOffset, $Label, $CaptionSettings); + } + + if ($Type == BOUND_MIN || $Type == BOUND_BOTH) { + if ($MinLabelPos == BOUND_LABEL_POS_TOP + || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) + ) { + $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + } + if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM + || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) + ) { + $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; + $Align = TEXT_ALIGN_TOPMIDDLE; + } + + $XPos = $X + $MinPos * $XStep + $SerieOffset; + $Label = sprintf( + '%s%s', + $MinLabelTxt, + $this->scaleFormat(round($MinValue, $Decimals), $Mode, $Format, $Unit) + ); + + $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label); + $XOffset = 0; + $YOffset = 0; + if ($TxtPos[0]["X"] < $this->GraphAreaX1) { + $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2); + } + if ($TxtPos[1]["X"] > $this->GraphAreaX2) { + $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2); + } + if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { + $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; + } + if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { + $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); + } + + $CaptionSettings["R"] = $MinDisplayR; + $CaptionSettings["G"] = $MinDisplayG; + $CaptionSettings["B"] = $MinDisplayB; + $CaptionSettings["Align"] = $Align; + + $this->drawText( + $XPos + $XOffset, + $YPos - $DisplayOffset + $YOffset, + $Label, + $CaptionSettings + ); + } + } else { + $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + $X = $this->GraphAreaY1 + $XMargin; + $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; + + if ($Type == BOUND_MAX || $Type == BOUND_BOTH) { + if ($MaxLabelPos == BOUND_LABEL_POS_TOP + || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) + ) { + $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; + $Align = TEXT_ALIGN_MIDDLELEFT; + } + if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM + || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) + ) { + $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; + $Align = TEXT_ALIGN_MIDDLERIGHT; + } + + $XPos = $X + $MaxPos * $XStep + $SerieOffset; + $Label = $MaxLabelTxt . $this->scaleFormat($MaxValue, $Mode, $Format, $Unit); + + $TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label); + $XOffset = 0; + $YOffset = 0; + if ($TxtPos[0]["X"] < $this->GraphAreaX1) { + $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; + } + if ($TxtPos[1]["X"] > $this->GraphAreaX2) { + $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); + } + if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { + $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2; + } + if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { + $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2); + } + + $CaptionSettings["R"] = $MaxDisplayR; + $CaptionSettings["G"] = $MaxDisplayG; + $CaptionSettings["B"] = $MaxDisplayB; + $CaptionSettings["Align"] = $Align; + + $this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings); + } + + if ($Type == BOUND_MIN || $Type == BOUND_BOTH) { + if ($MinLabelPos == BOUND_LABEL_POS_TOP + || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) + ) { + $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; + $Align = TEXT_ALIGN_MIDDLELEFT; + } + if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM + || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) + ) { + $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; + $Align = TEXT_ALIGN_MIDDLERIGHT; + } + + $XPos = $X + $MinPos * $XStep + $SerieOffset; + $Label = $MinLabelTxt . $this->scaleFormat($MinValue, $Mode, $Format, $Unit); + + $TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label); + $XOffset = 0; + $YOffset = 0; + if ($TxtPos[0]["X"] < $this->GraphAreaX1) { + $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; + } + if ($TxtPos[1]["X"] > $this->GraphAreaX2) { + $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); + } + if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { + $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2; + } + if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { + $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2); + } + + $CaptionSettings["R"] = $MinDisplayR; + $CaptionSettings["G"] = $MinDisplayG; + $CaptionSettings["B"] = $MinDisplayB; + $CaptionSettings["Align"] = $Align; + + $this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings); + } + } + } + } + } + + /** + * Write labels + * @param string $SeriesName + * @param array $Indexes + * @param array $Format + */ + public function writeLabel($SeriesName, $Indexes, array $Format = []) + { + $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : null; + $ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : null; + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + $DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : false; + $VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0; + $VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0; + $VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0; + $VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40; + $VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + if (!is_array($Indexes)) { + $Index = $Indexes; + $Indexes = []; + $Indexes[] = $Index; + } + if (!is_array($SeriesName)) { + $SerieName = $SeriesName; + $SeriesName = []; + $SeriesName[] = $SerieName; + } + if ($ForceLabels != null && !is_array($ForceLabels)) { + $ForceLabel = $ForceLabels; + $ForceLabels = []; + $ForceLabels[] = $ForceLabel; + } + + foreach ($Indexes as $Key => $Index) { + $Series = []; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin + $Index * $XStep; + + if ($DrawVerticalLine) { + $this->drawLine( + $X, + $this->GraphAreaY1 + $Data["YMargin"], + $X, + $this->GraphAreaY2 - $Data["YMargin"], + [ + "R" => $VerticalLineR, + "G" => $VerticalLineG, + "B" => $VerticalLineB, + "Alpha" => $VerticalLineAlpha, + "Ticks" => $VerticalLineTicks + ] + ); + } + + $MinY = $this->GraphAreaY2; + foreach ($SeriesName as $SerieName) { + if (isset($Data["Series"][$SerieName]["Data"][$Index])) { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $XAxisMode = $Data["XAxisDisplay"]; + $XAxisFormat = $Data["XAxisFormat"]; + $XAxisUnit = $Data["XAxisUnit"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + $XLabel = ""; + + if (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) + ) { + $XLabel = $this->scaleFormat( + $Data["Series"][$Data["Abscissa"]]["Data"][$Index], + $XAxisMode, + $XAxisFormat, + $XAxisUnit + ); + } + + if ($OverrideTitle != null) { + $Description = $OverrideTitle; + } elseif (count($SeriesName) == 1) { + $Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel; + } elseif (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) + ) { + $Description = $XLabel; + } + + $Serie = [ + "R" => $Data["Series"][$SerieName]["Color"]["R"], + "G" => $Data["Series"][$SerieName]["Color"]["G"], + "B" => $Data["Series"][$SerieName]["Color"]["B"], + "Alpha" => $Data["Series"][$SerieName]["Color"]["Alpha"] + ]; + if (count($SeriesName) == 1 + && isset($Data["Series"][$SerieName]["XOffset"]) + ) { + $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; + } else { + $SerieOffset = 0; + } + $Value = $Data["Series"][$SerieName]["Data"][$Index]; + if ($Value == VOID) { + $Value = "NaN"; + } + + if ($ForceLabels != null) { + $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; + } else { + $Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); + } + + if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) { + if ($Value >= 0) { + $LookFor = "+"; + } else { + $LookFor = "-"; + } + + $Value = 0; + $Done = false; + foreach ($Data["Series"] as $Name => $SerieLookup) { + if ($SerieLookup["isDrawable"] == true + && $Name != $Data["Abscissa"] && !$Done + ) { + if (isset($Data["Series"][$Name]["Data"][$Index]) + && $Data["Series"][$Name]["Data"][$Index] != VOID + ) { + if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") { + $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; + } + if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") { + $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; + } + if ($Name == $SerieName) { + $Done = true; + } + } + } + } + } + + $X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset); + $Y = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID])); + + if ($Y < $MinY) { + $MinY = $Y; + } + + if ($DrawPoint == LABEL_POINT_CIRCLE) { + $this->drawFilledCircle( + $X, + $Y, + 3, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } elseif ($DrawPoint == LABEL_POINT_BOX) { + $this->drawFilledRectangle( + $X - 2, + $Y - 2, + $X + 2, + $Y + 2, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } + $Series[] = ["Format" => $Serie, "Caption" => $Caption]; + } + } + $this->drawLabelBox($X, $MinY - 3, $Description, $Series, $Format); + } else { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep; + + if ($DrawVerticalLine) { + $this->drawLine( + $this->GraphAreaX1 + $Data["YMargin"], + $Y, + $this->GraphAreaX2 - $Data["YMargin"], + $Y, + [ + "R" => $VerticalLineR, + "G" => $VerticalLineG, + "B" => $VerticalLineB, + "Alpha" => $VerticalLineAlpha, + "Ticks" => $VerticalLineTicks + ] + ); + } + + $MinX = $this->GraphAreaX2; + foreach ($SeriesName as $Key => $SerieName) { + if (isset($Data["Series"][$SerieName]["Data"][$Index])) { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $XAxisMode = $Data["XAxisDisplay"]; + $XAxisFormat = $Data["XAxisFormat"]; + $XAxisUnit = $Data["XAxisUnit"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + $XLabel = ""; + + if (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) + ) { + $XLabel = $this->scaleFormat( + $Data["Series"][$Data["Abscissa"]]["Data"][$Index], + $XAxisMode, + $XAxisFormat, + $XAxisUnit + ); + } + + if ($OverrideTitle != null) { + $Description = $OverrideTitle; + } elseif (count($SeriesName) == 1) { + if (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) + ) { + $Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel; + } + } elseif (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) + ) { + $Description = $XLabel; + } + $Serie = []; + if (isset($Data["Extended"]["Palette"][$Index])) { + $Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"]; + $Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"]; + $Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"]; + $Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"]; + } else { + $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; + $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; + $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; + $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; + } + + if (count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"])) { + $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; + } else { + $SerieOffset = 0; + } + + $Value = $Data["Series"][$SerieName]["Data"][$Index]; + if ($ForceLabels != null) { + $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; + } else { + $Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); + } + if ($Value == VOID) { + $Value = "NaN"; + } + + if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) { + if ($Value >= 0) { + $LookFor = "+"; + } else { + $LookFor = "-"; + } + + $Value = 0; + $Done = false; + foreach ($Data["Series"] as $Name => $SerieLookup) { + if ($SerieLookup["isDrawable"] == true + && $Name != $Data["Abscissa"] + && !$Done + ) { + if (isset($Data["Series"][$Name]["Data"][$Index]) + && $Data["Series"][$Name]["Data"][$Index] != VOID + ) { + if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") { + $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; + } + if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") { + $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; + } + if ($Name == $SerieName) { + $Done = true; + } + } + } + } + } + + $X = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID])); + $Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset); + + if ($X < $MinX) { + $MinX = $X; + } + + if ($DrawPoint == LABEL_POINT_CIRCLE) { + $this->drawFilledCircle( + $X, + $Y, + 3, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } elseif ($DrawPoint == LABEL_POINT_BOX) { + $this->drawFilledRectangle( + $X - 2, + $Y - 2, + $X + 2, + $Y + 2, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } + $Series[] = ["Format" => $Serie, "Caption" => $Caption]; + } + } + $this->drawLabelBox($MinX, $Y - 3, $Description, $Series, $Format); + } + } + } + + /** + * @param GdImage|resource $image + * @param array $points + * @param int $numPoints + * @param int $color + * @return void + */ + protected function imageFilledPolygonWrapper( + $image, + array $points, + $numPoints, + $color + ) { + if (version_compare(PHP_VERSION, '8.1.0') === -1) { + imagefilledpolygon($image, $points, $numPoints, $color); + } else { + imagefilledpolygon($image, $points, $color); + } + } +} diff --git a/vendor/szymach/c-pchart/src/Cache.php b/vendor/szymach/c-pchart/src/Cache.php new file mode 100644 index 0000000..73dd42d --- /dev/null +++ b/vendor/szymach/c-pchart/src/Cache.php @@ -0,0 +1,392 @@ +CacheFolder = $CacheFolder; + $this->CacheIndex = $CacheIndex; + $this->CacheDB = $CacheDB; + if (!file_exists($this->CacheFolder . "/" . $this->CacheIndex)) { + touch($this->CacheFolder . "/" . $this->CacheIndex); + } + if (!file_exists($this->CacheFolder . "/" . $this->CacheDB)) { + touch($this->CacheFolder . "/" . $this->CacheDB); + } + } + + /** + * Flush the cache contents + */ + public function flush() + { + if (file_exists($this->CacheFolder . "/" . $this->CacheIndex)) { + unlink($this->CacheFolder . "/" . $this->CacheIndex); + touch($this->CacheFolder . "/" . $this->CacheIndex); + } + if (file_exists($this->CacheFolder . "/" . $this->CacheDB)) { + unlink($this->CacheFolder . "/" . $this->CacheDB); + touch($this->CacheFolder . "/" . $this->CacheDB); + } + } + + /** + * Return the MD5 of the data array to clearly identify the chart + * + * @param Data $Data + * @param string $Marker + * @return string + */ + public function getHash(Data $Data, $Marker = "") + { + return md5($Marker . serialize($Data->Data)); + } + + /** + * Write the generated picture to the cache + * + * @param string $ID + * @param Image $pChartObject + */ + public function writeToCache($ID, Image $pChartObject) + { + /* Compute the paths */ + $TemporaryFile = tempnam($this->CacheFolder, "tmp_"); + $Database = $this->CacheFolder . "/" . $this->CacheDB; + $Index = $this->CacheFolder . "/" . $this->CacheIndex; + /* Flush the picture to a temporary file */ + imagepng($pChartObject->Picture, $TemporaryFile); + + /* Retrieve the files size */ + $PictureSize = filesize($TemporaryFile); + $DBSize = filesize($Database); + + /* Save the index */ + $Handle = fopen($Index, "a"); + fwrite($Handle, $ID . "," . $DBSize . "," . $PictureSize . "," . time() . ",0\r\n"); + fclose($Handle); + + /* Get the picture raw contents */ + $Handle = fopen($TemporaryFile, "r"); + $Raw = fread($Handle, $PictureSize); + fclose($Handle); + + /* Save the picture in the solid database file */ + $Handle = fopen($Database, "a"); + fwrite($Handle, $Raw); + fclose($Handle); + + /* Remove temporary file */ + unlink($TemporaryFile); + } + + /** + * Remove object older than the specified TS + * @param int $Expiry + */ + public function removeOlderThan($Expiry) + { + $this->dbRemoval(["Expiry" => $Expiry]); + } + + /** + * Remove an object from the cache + * @param string $ID + */ + public function remove($ID) + { + $this->dbRemoval(["Name" => $ID]); + } + + /** + * Remove with specified criterias. + * + * @param array $Settings + * @return int + */ + public function dbRemoval(array $Settings) + { + $ID = isset($Settings["Name"]) ? $Settings["Name"] : null; + $Expiry = isset($Settings["Expiry"]) ? $Settings["Expiry"] : -(24 * 60 * 60); + $TS = time() - $Expiry; + + /* Compute the paths */ + $Database = $this->CacheFolder . "/" . $this->CacheDB; + $Index = $this->CacheFolder . "/" . $this->CacheIndex; + $DatabaseTemp = $this->CacheFolder . "/" . $this->CacheDB . ".tmp"; + $IndexTemp = $this->CacheFolder . "/" . $this->CacheIndex . ".tmp"; + + /* Single file removal */ + if ($ID != null) { + /* Retrieve object informations */ + $Object = $this->isInCache($ID, true); + + /* If it's not in the cache DB, go away */ + if (!$Object) { + return(0); + } + } + + /* Create the temporary files */ + if (!file_exists($DatabaseTemp)) { + touch($DatabaseTemp); + } + if (!file_exists($IndexTemp)) { + touch($IndexTemp); + } + + /* Open the file handles */ + $IndexHandle = @fopen($Index, "r"); + $IndexTempHandle = @fopen($IndexTemp, "w"); + $DBHandle = @fopen($Database, "r"); + $DBTempHandle = @fopen($DatabaseTemp, "w"); + + /* Remove the selected ID from the database */ + while (!feof($IndexHandle)) { + $Entry = fgets($IndexHandle, 4096); + $Entry = str_replace("\r", "", $Entry); + $Entry = str_replace("\n", "", $Entry); + $Settings = preg_split("/,/", $Entry); + + if ($Entry != "") { + $PicID = $Settings[0]; + $DBPos = $Settings[1]; + $PicSize = $Settings[2]; + $GeneratedTS = $Settings[3]; + $Hits = $Settings[4]; + + if ($Settings[0] != $ID && $GeneratedTS > $TS) { + $CurrentPos = ftell($DBTempHandle); + fwrite( + $IndexTempHandle, + sprintf( + "%s,%s,%s,%s,%s\r\n", + $PicID, + $CurrentPos, + $PicSize, + $GeneratedTS, + $Hits + ) + ); + + fseek($DBHandle, $DBPos); + $Picture = fread($DBHandle, $PicSize); + fwrite($DBTempHandle, $Picture); + } + } + } + + /* Close the handles */ + fclose($IndexHandle); + fclose($IndexTempHandle); + fclose($DBHandle); + fclose($DBTempHandle); + + /* Remove the prod files */ + unlink($Database); + unlink($Index); + + /* Swap the temp & prod DB */ + rename($DatabaseTemp, $Database); + rename($IndexTemp, $Index); + } + + /** + * Is the file in cache? + * + * @param string $ID + * @param boolean $Verbose + * @param boolean $UpdateHitsCount + * @return boolean + */ + public function isInCache($ID, $Verbose = false, $UpdateHitsCount = false) + { + /* Compute the paths */ + $Index = $this->CacheFolder . "/" . $this->CacheIndex; + + /* Search the picture in the index file */ + $Handle = @fopen($Index, "r"); + while (!feof($Handle)) { + $IndexPos = ftell($Handle); + $Entry = fgets($Handle, 4096); + if ($Entry != "") { + $Settings = preg_split("/,/", $Entry); + $PicID = $Settings[0]; + if ($PicID == $ID) { + fclose($Handle); + + $DBPos = $Settings[1]; + $PicSize = $Settings[2]; + $GeneratedTS = $Settings[3]; + $Hits = intval($Settings[4]); + + if ($UpdateHitsCount) { + $Hits++; + if (strlen($Hits) < 7) { + $Hits = $Hits . str_repeat(" ", 7 - strlen($Hits)); + } + + $Handle = @fopen($Index, "r+"); + fseek($Handle, $IndexPos); + fwrite( + $Handle, + sprintf( + "%s,%s,%s,%s,%s\r\n", + $PicID, + $DBPos, + $PicSize, + $GeneratedTS, + $Hits + ) + ); + fclose($Handle); + } + + if ($Verbose) { + return [ + "DBPos" => $DBPos, + "PicSize" => $PicSize, + "GeneratedTS" => $GeneratedTS, + "Hits" => $Hits + ]; + } else { + return true; + } + } + } + } + fclose($Handle); + + /* Picture isn't in the cache */ + return false; + } + + /** + * Automatic output method based on the calling interface + * @param string $ID + * @param string $Destination + */ + public function autoOutput($ID, $Destination = "output.png") + { + if (php_sapi_name() == "cli") { + $this->saveFromCache($ID, $Destination); + } else { + $this->strokeFromCache($ID); + } + } + + /** + * Show image from cache + * @param string $ID + * @return boolean + */ + public function strokeFromCache($ID) + { + /* Get the raw picture from the cache */ + $Picture = $this->getFromCache($ID); + + /* Do we have a hit? */ + if ($Picture == null) { + return false; + } + + header('Content-type: image/png'); + echo $Picture; + + return true; + } + + /** + * Save file from cache. + * @param string $ID + * @param string $Destination + * @return boolean + */ + public function saveFromCache($ID, $Destination) + { + /* Get the raw picture from the cache */ + $Picture = $this->getFromCache($ID); + + /* Do we have a hit? */ + if ($Picture == null) { + return false; + } + + /* Flush the picture to a file */ + $Handle = fopen($Destination, "w"); + fwrite($Handle, $Picture); + fclose($Handle); + + /* All went fine */ + return true; + } + + /** + * Get file from cache + * @param string $ID + * @return string|null + */ + public function getFromCache($ID) + { + /* Compute the path */ + $Database = $this->CacheFolder . "/" . $this->CacheDB; + + /* Lookup for the picture in the cache */ + $CacheInfo = $this->isInCache($ID, true, true); + + /* Not in the cache */ + if (!$CacheInfo) { + return null; + } + + /* Get the database extended information */ + $DBPos = $CacheInfo["DBPos"]; + $PicSize = $CacheInfo["PicSize"]; + + /* Extract the picture from the solid cache file */ + $Handle = @fopen($Database, "r"); + fseek($Handle, $DBPos); + $Picture = fread($Handle, $PicSize); + fclose($Handle); + + /* Return back the raw picture data */ + return $Picture; + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Bubble.php b/vendor/szymach/c-pchart/src/Chart/Bubble.php new file mode 100644 index 0000000..e5faa20 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Bubble.php @@ -0,0 +1,532 @@ +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /** + * Prepare the scale + * + * @param mixed $DataSeries + * @param mixed $WeightSeries + */ + public function bubbleScale($DataSeries, $WeightSeries) + { + if (!is_array($DataSeries)) { + $DataSeries = [$DataSeries]; + } + if (!is_array($WeightSeries)) { + $WeightSeries = [$WeightSeries]; + } + + /* Parse each data series to find the new min & max boundaries to scale */ + $NewPositiveSerie = []; + $NewNegativeSerie = []; + $MaxValues = 0; + $LastPositive = 0; + $LastNegative = 0; + + foreach ($DataSeries as $Key => $SerieName) { + $SerieWeightName = $WeightSeries[$Key]; + + $this->pDataObject->setSerieDrawable($SerieWeightName, false); + $serieData = $this->pDataObject->Data["Series"][$SerieName]["Data"]; + if (count($serieData) > $MaxValues) { + $MaxValues = count($serieData); + } + + foreach ($serieData as $Key => $Value) { + if ($Value >= 0) { + $BubbleBounds = $Value + $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; + + if (!isset($NewPositiveSerie[$Key])) { + $NewPositiveSerie[$Key] = $BubbleBounds; + } elseif ($NewPositiveSerie[$Key] < $BubbleBounds) { + $NewPositiveSerie[$Key] = $BubbleBounds; + } + $LastPositive = $BubbleBounds; + } else { + $BubbleBounds = $Value - $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; + + if (!isset($NewNegativeSerie[$Key])) { + $NewNegativeSerie[$Key] = $BubbleBounds; + } elseif ($NewNegativeSerie[$Key] > $BubbleBounds) { + $NewNegativeSerie[$Key] = $BubbleBounds; + } + $LastNegative = $BubbleBounds; + } + } + } + + /* Check for missing values and all the fake positive serie */ + if (count($NewPositiveSerie)) { + for ($i = 0; $i < $MaxValues; $i++) { + if (!isset($NewPositiveSerie[$i])) { + $NewPositiveSerie[$i] = $LastPositive; + } + } + $this->pDataObject->addPoints($NewPositiveSerie, "BubbleFakePositiveSerie"); + } + + /* Check for missing values and all the fake negative serie */ + if (count($NewNegativeSerie)) { + for ($i = 0; $i < $MaxValues; $i++) { + if (!isset($NewNegativeSerie[$i])) { + $NewNegativeSerie[$i] = $LastNegative; + } + } + + $this->pDataObject->addPoints($NewNegativeSerie, "BubbleFakeNegativeSerie"); + } + } + + public function resetSeriesColors() + { + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + $ID = 0; + foreach ($Data["Series"] as $SerieName => $SeriesParameters) { + if ($SeriesParameters["isDrawable"]) { + $this->pDataObject->Data["Series"][$SerieName]["Color"]["R"] = $Palette[$ID]["R"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["G"] = $Palette[$ID]["G"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["B"] = $Palette[$ID]["B"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["Alpha"] = $Palette[$ID]["Alpha"]; + $ID++; + } + } + } + + /* Prepare the scale */ + + public function drawBubbleChart($DataSeries, $WeightSeries, $Format = "") + { + $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : VOID; + $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : true; + $BorderWidth = isset($Format["BorderWidth"]) ? $Format["BorderWidth"] : 1; + $Shape = isset($Format["Shape"]) ? $Format["Shape"] : BUBBLE_SHAPE_ROUND; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + if (!is_array($DataSeries)) { + $DataSeries = [$DataSeries]; + } + if (!is_array($WeightSeries)) { + $WeightSeries = [$WeightSeries]; + } + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + if (isset($Data["Series"]["BubbleFakePositiveSerie"])) { + $this->pDataObject->setSerieDrawable("BubbleFakePositiveSerie", false); + } + if (isset($Data["Series"]["BubbleFakeNegativeSerie"])) { + $this->pDataObject->setSerieDrawable("BubbleFakeNegativeSerie", false); + } + + $this->resetSeriesColors(); + + list($XMargin, $XDivs) = $this->pChartObject->scaleGetXSettings(); + + foreach ($DataSeries as $Key => $SerieName) { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Data["Series"][$SerieName]["Description"])) { + $SerieDescription = $Data["Series"][$SerieName]["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; + + $X = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $this->pChartObject->GraphAreaY1 + $XMargin; + + $Color = [ + "R" => $Palette[$Key]["R"], + "G" => $Palette[$Key]["G"], + "B" => $Palette[$Key]["B"], + "Alpha" => $Palette[$Key]["Alpha"] + ]; + + if ($ForceAlpha != VOID) { + $Color["Alpha"] = $ForceAlpha; + } + + if ($DrawBorder) { + if ($BorderWidth != 1) { + if ($Surrounding != null) { + $BorderR = $Palette[$Key]["R"] + $Surrounding; + $BorderG = $Palette[$Key]["G"] + $Surrounding; + $BorderB = $Palette[$Key]["B"] + $Surrounding; + } + if ($ForceAlpha != VOID) { + $BorderAlpha = $ForceAlpha / 2; + } + $BorderColor = [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha + ]; + } else { + $Color["BorderAlpha"] = $BorderAlpha; + + if ($Surrounding != null) { + $Color["BorderR"] = $Palette[$Key]["R"] + $Surrounding; + $Color["BorderG"] = $Palette[$Key]["G"] + $Surrounding; + $Color["BorderB"] = $Palette[$Key]["B"] + $Surrounding; + } else { + $Color["BorderR"] = $BorderR; + $Color["BorderG"] = $BorderG; + $Color["BorderB"] = $BorderB; + } + if ($ForceAlpha != VOID) { + $Color["BorderAlpha"] = $ForceAlpha / 2; + } + } + } + + foreach ($Data["Series"][$SerieName]["Data"] as $iKey => $Point) { + $Weight = $Point + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]; + + $PosArray = $this->pChartObject->scaleComputeY( + $Point, + ["AxisID" => $AxisID] + ); + $WeightArray = $this->pChartObject->scaleComputeY( + $Weight, + ["AxisID" => $AxisID] + ); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) + / $XDivs + ; + } + $Y = floor($PosArray); + $CircleRadius = floor(abs($PosArray - $WeightArray) / 2); + + if ($Shape == BUBBLE_SHAPE_SQUARE) { + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + ( + floor($X - $CircleRadius) + . "," . floor($Y - $CircleRadius) + . "," . floor($X + $CircleRadius) + . "," . floor($Y + $CircleRadius) + ), + $this->pChartObject->toHTMLColor( + $Palette[$Key]["R"], + $Palette[$Key]["G"], + $Palette[$Key]["B"] + ), + $SerieDescription, + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey] + ); + } + + if ($BorderWidth != 1) { + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius - $BorderWidth, + $Y - $CircleRadius - $BorderWidth, + $X + $CircleRadius + $BorderWidth, + $Y + $CircleRadius + $BorderWidth, + $BorderColor + ); + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius, + $Y - $CircleRadius, + $X + $CircleRadius, + $Y + $CircleRadius, + $Color + ); + } else { + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius, + $Y - $CircleRadius, + $X + $CircleRadius, + $Y + $CircleRadius, + $Color + ); + } + } elseif ($Shape == BUBBLE_SHAPE_ROUND) { + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . floor($CircleRadius), + $this->pChartObject->toHTMLColor( + $Palette[$Key]["R"], + $Palette[$Key]["G"], + $Palette[$Key]["B"] + ), + $SerieDescription, + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey] + ); + } + + if ($BorderWidth != 1) { + $this->pChartObject->drawFilledCircle( + $X, + $Y, + $CircleRadius + $BorderWidth, + $BorderColor + ); + $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); + } else { + $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); + } + } + + $X = $X + $XStep; + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $XMargin * 2) + / $XDivs + ; + } + $X = floor($PosArray); + $CircleRadius = floor(abs($PosArray - $WeightArray) / 2); + + if ($Shape == BUBBLE_SHAPE_SQUARE) { + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + ( + floor($X - $CircleRadius) . "," + . floor($Y - $CircleRadius) . "," + . floor($X + $CircleRadius) . "," + . floor($Y + $CircleRadius) + ), + $this->pChartObject->toHTMLColor( + $Palette[$Key]["R"], + $Palette[$Key]["G"], + $Palette[$Key]["B"] + ), + $SerieDescription, + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey] + ); + } + + if ($BorderWidth != 1) { + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius - $BorderWidth, + $Y - $CircleRadius - $BorderWidth, + $X + $CircleRadius + $BorderWidth, + $Y + $CircleRadius + $BorderWidth, + $BorderColor + ); + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius, + $Y - $CircleRadius, + $X + $CircleRadius, + $Y + $CircleRadius, + $Color + ); + } else { + $this->pChartObject->drawFilledRectangle( + $X - $CircleRadius, + $Y - $CircleRadius, + $X + $CircleRadius, + $Y + $CircleRadius, + $Color + ); + } + } elseif ($Shape == BUBBLE_SHAPE_ROUND) { + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . floor($CircleRadius), + $this->pChartObject->toHTMLColor( + $Palette[$Key]["R"], + $Palette[$Key]["G"], + $Palette[$Key]["B"] + ), + $SerieDescription, + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey] + ); + } + + if ($BorderWidth != 1) { + $this->pChartObject->drawFilledCircle( + $X, + $Y, + $CircleRadius + $BorderWidth, + $BorderColor + ); + $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); + } else { + $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); + } + } + + $Y = $Y + $XStep; + } + } + } + } + + public function writeBubbleLabel($SerieName, $SerieWeightName, $Points, $Format = "") + { + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + + if (!is_array($Points)) { + $Point = $Points; + $Points = []; + $Points[] = $Point; + } + + $Data = $this->pDataObject->getData(); + + if (!isset($Data["Series"][$SerieName]) + || !isset($Data["Series"][$SerieWeightName]) + ) { + return(0); + } + list($XMargin, $XDivs) = $this->pChartObject->scaleGetXSettings(); + + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; + + $X = $InitialX = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $InitialY = $this->pChartObject->GraphAreaY1 + $XMargin; + + $Color = [ + "R" => $Data["Series"][$SerieName]["Color"]["R"], + "G" => $Data["Series"][$SerieName]["Color"]["G"], + "B" => $Data["Series"][$SerieName]["Color"]["B"], + "Alpha" => $Data["Series"][$SerieName]["Color"]["Alpha"] + ]; + + foreach ($Points as $Key => $Point) { + $Value = $Data["Series"][$SerieName]["Data"][$Point]; + $PosArray = $this->pChartObject->scaleComputeY($Value, ["AxisID" => $AxisID]); + + if (isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Point])) { + $Abscissa = $Data["Series"][$Data["Abscissa"]]["Data"][$Point] . " : "; + } else { + $Abscissa = ""; + } + $Value = $this->pChartObject->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); + $Weight = $Data["Series"][$SerieWeightName]["Data"][$Point]; + $Caption = $Abscissa . $Value . " / " . $Weight; + + if (isset($Data["Series"][$SerieName]["Description"])) { + $Description = $Data["Series"][$SerieName]["Description"]; + } else { + $Description = "No description"; + } + $Series = [["Format" => $Color, "Caption" => $Caption]]; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) + / $XDivs + ; + } + + $X = floor($InitialX + $Point * $XStep); + $Y = floor($PosArray); + } else { + if ($XDivs == 0) { + $YStep = 0; + } else { + $YStep = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $XMargin * 2) + / $XDivs + ; + } + + $X = floor($PosArray); + $Y = floor($InitialY + $Point * $YStep); + } + + if ($DrawPoint == LABEL_POINT_CIRCLE) { + $this->pChartObject->drawFilledCircle( + $X, + $Y, + 3, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } elseif ($DrawPoint == LABEL_POINT_BOX) { + $this->pChartObject->drawFilledRectangle( + $X - 2, + $Y - 2, + $X + 2, + $Y + 2, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } + + $this->pChartObject->drawLabelBox($X, $Y - 3, $Description, $Series, $Format); + } + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Indicator.php b/vendor/szymach/c-pchart/src/Chart/Indicator.php new file mode 100644 index 0000000..1105f24 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Indicator.php @@ -0,0 +1,377 @@ +pChartObject = $pChartObject; + } + + /** + * Draw an indicator + * + * @param int $X + * @param int $Y + * @param int $Width + * @param int $Height + * @param array $Format + * @return null|int + */ + public function draw($X, $Y, $Width, $Height, array $Format = []) + { + $Values = isset($Format["Values"]) ? $Format["Values"] : VOID; + $IndicatorSections = isset($Format["IndicatorSections"]) ? $Format["IndicatorSections"] : null; + $ValueDisplay = isset($Format["ValueDisplay"]) ? $Format["ValueDisplay"] : INDICATOR_VALUE_BUBBLE; + $SectionsMargin = isset($Format["SectionsMargin"]) ? $Format["SectionsMargin"] : 4; + $DrawLeftHead = isset($Format["DrawLeftHead"]) ? $Format["DrawLeftHead"] : true; + $DrawRightHead = isset($Format["DrawRightHead"]) ? $Format["DrawRightHead"] : true; + $HeadSize = isset($Format["HeadSize"]) ? $Format["HeadSize"] : floor($Height / 4); + $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 4; + $CaptionLayout = isset($Format["CaptionLayout"]) ? $Format["CaptionLayout"] : INDICATOR_CAPTION_EXTENDED; + $CaptionPosition = isset($Format["CaptionPosition"]) ? $Format["CaptionPosition"] : INDICATOR_CAPTION_INSIDE; + $CaptionColorFactor = isset($Format["CaptionColorFactor"]) ? $Format["CaptionColorFactor"] : null; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $SubCaptionColorFactor = isset($Format["SubCaptionColorFactor"]) ? $Format["SubCaptionColorFactor"] : null; + $SubCaptionR = isset($Format["SubCaptionR"]) ? $Format["SubCaptionR"] : 50; + $SubCaptionG = isset($Format["SubCaptionG"]) ? $Format["SubCaptionG"] : 50; + $SubCaptionB = isset($Format["SubCaptionB"]) ? $Format["SubCaptionB"] : 50; + $SubCaptionAlpha = isset($Format["SubCaptionAlpha"]) ? $Format["SubCaptionAlpha"] : 100; + $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; + $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; + $CaptionFontName = isset($Format["CaptionFontName"]) + ? $Format["CaptionFontName"] : $this->pChartObject->FontName + ; + $CaptionFontSize = isset($Format["CaptionFontSize"]) + ? $Format["CaptionFontSize"] : $this->pChartObject->FontSize + ; + $Unit = isset($Format["Unit"]) ? $Format["Unit"] : ""; + + /* Convert the Values to display to an array if needed */ + if (!is_array($Values)) { + $Values = [$Values]; + } + + /* No section, let's die */ + if ($IndicatorSections == null) { + return 0; + } + + /* Determine indicator visual configuration */ + $OverallMin = $IndicatorSections[0]["End"]; + $OverallMax = $IndicatorSections[0]["Start"]; + foreach ($IndicatorSections as $Key => $Settings) { + if ($Settings["End"] > $OverallMax) { + $OverallMax = $Settings["End"]; + } + if ($Settings["Start"] < $OverallMin) { + $OverallMin = $Settings["Start"]; + } + } + $RealWidth = $Width - (count($IndicatorSections) - 1) * $SectionsMargin; + $XScale = $RealWidth / ($OverallMax - $OverallMin); + + $X1 = $X; + $ValuesPos = []; + foreach ($IndicatorSections as $Key => $Settings) { + $Color = ["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]]; + $Caption = $Settings["Caption"]; + $SubCaption = $Settings["Start"] . " - " . $Settings["End"]; + + $X2 = $X1 + ($Settings["End"] - $Settings["Start"]) * $XScale; + + if ($Key == 0 && $DrawLeftHead) { + $Poly = []; + $Poly[] = $X1 - 1; + $Poly[] = $Y; + $Poly[] = $X1 - 1; + $Poly[] = $Y + $Height; + $Poly[] = $X1 - 1 - $HeadSize; + $Poly[] = $Y + ($Height / 2); + $this->pChartObject->drawPolygon($Poly, $Color); + $this->pChartObject->drawLine($X1 - 2, $Y, $X1 - 2 - $HeadSize, $Y + ($Height / 2), $Color); + $this->pChartObject->drawLine( + $X1 - 2, + $Y + $Height, + $X1 - 2 - $HeadSize, + $Y + ($Height / 2), + $Color + ); + } + + /* Determine the position of the breaks */ + $Break = []; + foreach ($Values as $iKey => $Value) { + if ($Value >= $Settings["Start"] && $Value <= $Settings["End"]) { + $XBreak = $X1 + ($Value - $Settings["Start"]) * $XScale; + $ValuesPos[$Value] = $XBreak; + $Break[] = floor($XBreak); + } + } + + if ($ValueDisplay == INDICATOR_VALUE_LABEL) { + if (!count($Break)) { + $this->pChartObject->drawFilledRectangle($X1, $Y, $X2, $Y + $Height, $Color); + } else { + sort($Break); + $Poly = []; + $Poly[] = $X1; + $Poly[] = $Y; + $LastPointWritten = false; + foreach ($Break as $iKey => $Value) { + if ($Value - 5 >= $X1) { + $Poly[] = $Value - 5; + $Poly[] = $Y; + } elseif ($X1 - ($Value - 5) > 0) { + $Offset = $X1 - ($Value - 5); + $Poly = [$X1, $Y + $Offset]; + } + + $Poly[] = $Value; + $Poly[] = $Y + 5; + + if ($Value + 5 <= $X2) { + $Poly[] = $Value + 5; + $Poly[] = $Y; + } elseif (($Value + 5) > $X2) { + $Offset = ($Value + 5) - $X2; + $Poly[] = $X2; + $Poly[] = $Y + $Offset; + $LastPointWritten = true; + } + } + if (!$LastPointWritten) { + $Poly[] = $X2; + $Poly[] = $Y; + } + $Poly[] = $X2; + $Poly[] = $Y + $Height; + $Poly[] = $X1; + $Poly[] = $Y + $Height; + + $this->pChartObject->drawPolygon($Poly, $Color); + } + } else { + $this->pChartObject->drawFilledRectangle($X1, $Y, $X2, $Y + $Height, $Color); + } + + if ($Key == count($IndicatorSections) - 1 && $DrawRightHead) { + $Poly = []; + $Poly[] = $X2 + 1; + $Poly[] = $Y; + $Poly[] = $X2 + 1; + $Poly[] = $Y + $Height; + $Poly[] = $X2 + 1 + $HeadSize; + $Poly[] = $Y + ($Height / 2); + $this->pChartObject->drawPolygon($Poly, $Color); + $this->pChartObject->drawLine($X2 + 1, $Y, $X2 + 1 + $HeadSize, $Y + ($Height / 2), $Color); + $this->pChartObject->drawLine( + $X2 + 1, + $Y + $Height, + $X2 + 1 + $HeadSize, + $Y + ($Height / 2), + $Color + ); + } + + $YOffset = 0; + $XOffset = 0; + if ($CaptionPosition == INDICATOR_CAPTION_INSIDE) { + $TxtPos = $this->pChartObject->getTextBox( + $X1, + $Y + $Height + $TextPadding, + $CaptionFontName, + $CaptionFontSize, + 0, + $Caption + ); + $YOffset = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding; + + if ($CaptionLayout == INDICATOR_CAPTION_EXTENDED) { + $TxtPos = $this->pChartObject->getTextBox( + $X1, + $Y + $Height + $TextPadding, + $CaptionFontName, + $CaptionFontSize, + 0, + $SubCaption + ); + $YOffset = $YOffset + ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding * 2; + } + $XOffset = $TextPadding; + } + + if ($CaptionColorFactor == null) { + $CaptionColor = [ + "Align" => TEXT_ALIGN_TOPLEFT, + "FontName" => $CaptionFontName, + "FontSize" => $CaptionFontSize, + "R" => $CaptionR, + "G" => $CaptionG, + "B" => $CaptionB, + "Alpha" => $CaptionAlpha + ]; + } else { + $CaptionColor = [ + "Align" => TEXT_ALIGN_TOPLEFT, + "FontName" => $CaptionFontName, + "FontSize" => $CaptionFontSize, + "R" => $Settings["R"] + $CaptionColorFactor, + "G" => $Settings["G"] + $CaptionColorFactor, + "B" => $Settings["B"] + $CaptionColorFactor + ]; + } + + if ($SubCaptionColorFactor == null) { + $SubCaptionColor = [ + "Align" => TEXT_ALIGN_TOPLEFT, + "FontName" => $CaptionFontName, + "FontSize" => $CaptionFontSize, + "R" => $SubCaptionR, + "G" => $SubCaptionG, + "B" => $SubCaptionB, + "Alpha" => $SubCaptionAlpha + ]; + } else { + $SubCaptionColor = [ + "Align" => TEXT_ALIGN_TOPLEFT, + "FontName" => $CaptionFontName, + "FontSize" => $CaptionFontSize, + "R" => $Settings["R"] + $SubCaptionColorFactor, + "G" => $Settings["G"] + $SubCaptionColorFactor, + "B" => $Settings["B"] + $SubCaptionColorFactor + ]; + } + + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + + if ($CaptionLayout == INDICATOR_CAPTION_DEFAULT) { + $this->pChartObject->drawText($X1, $Y + $Height + $TextPadding, $Caption, $CaptionColor); + } elseif ($CaptionLayout == INDICATOR_CAPTION_EXTENDED) { + $TxtPos = $this->pChartObject->getTextBox( + $X1, + $Y + $Height + $TextPadding, + $CaptionFontName, + $CaptionFontSize, + 0, + $Caption + ); + $CaptionHeight = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; + + $this->pChartObject->drawText( + $X1 + $XOffset, + $Y + $Height - $YOffset + $TextPadding, + $Caption, + $CaptionColor + ); + $this->pChartObject->drawText( + $X1 + $XOffset, + $Y + $Height - $YOffset + $CaptionHeight + $TextPadding * 2, + $SubCaption, + $SubCaptionColor + ); + } + + $this->pChartObject->Shadow = $RestoreShadow; + + $X1 = $X2 + $SectionsMargin; + } + + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + + foreach ($Values as $Key => $Value) { + if ($Value >= $OverallMin && $Value <= $OverallMax) { + foreach ($IndicatorSections as $Key => $Settings) { + if ($Value >= $Settings["Start"] && $Value <= $Settings["End"]) { + $X1 = $ValuesPos[$Value]; //$X + $Key*$SectionsMargin + ($Value - $OverallMin) * $XScale; + + if ($ValueDisplay == INDICATOR_VALUE_BUBBLE) { + $TxtPos = $this->pChartObject->getTextBox( + $X1, + $Y, + $ValueFontName, + $ValueFontSize, + 0, + $Value . $Unit + ); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $TextPadding * 4) / 2); + + $this->pChartObject->drawFilledCircle( + $X1, + $Y, + $Radius + 4, + [ + "R" => $Settings["R"] + 20, + "G" => $Settings["G"] + 20, + "B" => $Settings["B"] + 20 + ] + ); + $this->pChartObject->drawFilledCircle( + $X1, + $Y, + $Radius, + ["R" => 255, "G" => 255, "B" => 255] + ); + + $TextSettings = [ + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontName" => $ValueFontName, + "FontSize" => $ValueFontSize + ]; + $this->pChartObject->drawText($X1 - 1, $Y - 1, $Value . $Unit, $TextSettings); + } elseif ($ValueDisplay == INDICATOR_VALUE_LABEL) { + $Caption = [ + [ + "Format" => [ + "R" => $Settings["R"], + "G" => $Settings["G"], + "B" => $Settings["B"], + "Alpha" => 100 + ], + "Caption" => $Value . $Unit + ] + ]; + $this->pChartObject->drawLabelBox( + floor($X1), + floor($Y) + 2, + "Value - " . $Settings["Caption"], + $Caption + ); + } + } + $X1 = $X2 + $SectionsMargin; + } + } + } + $this->pChartObject->Shadow = $RestoreShadow; + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Pie.php b/vendor/szymach/c-pchart/src/Chart/Pie.php new file mode 100644 index 0000000..907eed8 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Pie.php @@ -0,0 +1,2320 @@ +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /** + * Draw a pie chart + * @param int $X + * @param int $Y + * @param array $Format + * @return int + */ + public function draw2DPie($X, $Y, array $Format = []) + { + $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 60; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; + $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : true; + $Border = isset($Format["Border"]) ? $Format["Border"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ($Data["Abscissa"] == "") { + return PIE_NO_ABSCISSA; + } + + /* Try to find the data serie */ + $DataSerie = null; + foreach (array_keys($Data["Series"]) as $SerieName) { + if ($SerieName != $Data["Abscissa"]) { + $DataSerie = $SerieName; + } + } + + /* Do we have data to compute? */ + if (!$DataSerie) { + return PIE_NO_DATASERIE; + } + + /* Remove unused data */ + list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ($SerieSum == 0) { + return PIE_SUMISNULL; + } + + /* Dump the real number of data to draw */ + $Values = []; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if ($Value != 0) { + $Values[] = $Value; + } + } + + /* Compute the wasted angular space between series */ + if (count($Values) == 1) { + $WastedAngular = 0; + } else { + $WastedAngular = count($Values) * $DataGapAngle; + } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ($this->pChartObject->Shadow) { + $this->pChartObject->Shadow = false; + + $ShadowFormat = $Format; + $ShadowFormat["Shadow"] = true; + $this->draw2DPie( + $X + $this->pChartObject->ShadowX, + $Y + $this->pChartObject->ShadowY, + $ShadowFormat + ); + } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; + $ID = 0; + foreach ($Values as $Key => $Value) { + if ($Shadow) { + $Settings = [ + "R" => $this->pChartObject->ShadowR, + "G" => $this->pChartObject->ShadowG, + "B" => $this->pChartObject->ShadowB, + "Alpha" => $this->pChartObject->Shadowa + ]; + } else { + if (!isset($Palette[$ID]["R"])) { + $Color = $this->pChartObject->getRandomColor(); + $Palette[$ID] = $Color; + $this->pDataObject->savePalette($ID, $Color); + } + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } + + if (!$SecondPass && !$Shadow) { + if (!$Border) { + $Settings["Surrounding"] = 10; + } else { + $Settings["BorderR"] = $BorderR; + $Settings["BorderG"] = $BorderG; + $Settings["BorderB"] = $BorderB; + } + } + + $EndAngle = $Offset + ($Value * $ScaleFactor); + if ($EndAngle > 360) { + $EndAngle = 360; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + if ($DataGapAngle == 0) { + $X0 = $X; + $Y0 = $Y; + } else { + $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X; + $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y; + } + + $Plots = [$X0, $Y0]; + + for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) { + $Xc = cos(($i - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($i - 90) * PI / 180) * $Radius + $Y; + + if ($SecondPass && ($i < 90)) { + $Yc++; + } + if ($SecondPass && ($i > 180 && $i < 270)) { + $Xc++; + } + if ($SecondPass && ($i >= 270)) { + $Xc++; + $Yc++; + } + + $Plots[] = $Xc; + $Plots[] = $Yc; + } + + $this->pChartObject->drawPolygon($Plots, $Settings); + if ($RecordImageMap && !$Shadow) { + $this->pChartObject->addToImageMap( + "POLY", + $this->arraySerialize($Plots), + $this->pChartObject->toHTMLColor( + $Palette[$ID]["R"], + $Palette[$ID]["G"], + $Palette[$ID]["B"] + ), + $Data["Series"][$Data["Abscissa"]]["Data"][$Key], + $Value + ); + } + + if ($DrawLabels && !$Shadow && !$SecondPass) { + if ($LabelColor == PIE_LABEL_COLOR_AUTO) { + $Settings = [ + "FillR" => $Palette[$ID]["R"], + "FillG" => $Palette[$ID]["G"], + "FillB" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "FillR" => $LabelR, + "FillG" => $LabelG, + "FillB" => $LabelB, + "Alpha" => $LabelAlpha + ]; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ($LabelStacked) { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius); + } else { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false); + } + } + + $Offset = $i + $DataGapAngle; + $ID++; + } + + /* Second pass to smooth the angles */ + if ($SecondPass) { + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; + $ID = 0; + foreach ($Values as $Key => $Value) { + $FirstPoint = true; + if ($Shadow) { + $Settings = [ + "R" => $this->pChartObject->ShadowR, + "G" => $this->pChartObject->ShadowG, + "B" => $this->pChartObject->ShadowB, + "Alpha" => $this->pChartObject->Shadowa + ]; + } else { + if ($Border) { + $Settings = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB]; + } else { + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } + } + + $EndAngle = $Offset + ($Value * $ScaleFactor); + if ($EndAngle > 360) { + $EndAngle = 360; + } + + if ($DataGapAngle == 0) { + $X0 = $X; + $Y0 = $Y; + } else { + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X; + $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y; + } + $Plots[] = $X0; + $Plots[] = $Y0; + + for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) { + $Xc = cos(($i - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($i - 90) * PI / 180) * $Radius + $Y; + + if ($FirstPoint) { + $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings); + $FirstPoint = false; + } + + $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings); + } + $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings); + + if ($DrawLabels && !$Shadow) { + if ($LabelColor == PIE_LABEL_COLOR_AUTO) { + $Settings = [ + "FillR" => $Palette[$ID]["R"], + "FillG" => $Palette[$ID]["G"], + "FillB" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "FillR" => $LabelR, + "FillG" => $LabelG, + "FillB" => $LabelB, + "Alpha" => $LabelAlpha + ]; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ($LabelStacked) { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius); + } else { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false); + } + } + + $Offset = $i + $DataGapAngle; + $ID++; + } + } + + if ($WriteValues != null && !$Shadow) { + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; + $ID = count($Values) - 1; + $Settings = [ + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "R" => $ValueR, + "G" => $ValueG, + "B" => $ValueB, + "Alpha" => $ValueAlpha + ]; + foreach ($Values as $Key => $Value) { + $EndAngle = ($Value * $ScaleFactor) + $Offset; + if ((int) $EndAngle > 360) { + $EndAngle = 0; + } + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + + if ($ValuePosition == PIE_VALUE_OUTSIDE) { + $Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X; + $Yc = sin(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $Y; + } else { + $Xc = cos(($Angle - 90) * PI / 180) * ($Radius) / 2 + $X; + $Yc = sin(($Angle - 90) * PI / 180) * ($Radius) / 2 + $Y; + } + + if ($WriteValues == PIE_VALUE_PERCENTAGE) { + $Display = round((100 / $SerieSum) * $Value, $Precision) . "%"; + } elseif ($WriteValues == PIE_VALUE_NATURAL) { + $Display = $Value . $ValueSuffix; + } + $this->pChartObject->drawText($Xc, $Yc, $Display, $Settings); + + $Offset = $EndAngle + $DataGapAngle; + $ID--; + } + } + + if ($DrawLabels && $LabelStacked) { + $this->writeShiftedLabels(); + } + + $this->pChartObject->Shadow = $RestoreShadow; + + return PIE_RENDERED; + } + + /** + * Draw a 3D pie chart + * @param int $X + * @param int $Y + * @param array $Format + * @return int + */ + public function draw3DPie($X, $Y, array $Format = []) + { + /* Rendering layout */ + $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 80; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .5; + $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 20; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; + $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : true; + $Border = isset($Format["Border"]) ? $Format["Border"] : false; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null; //PIE_VALUE_PERCENTAGE + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_INSIDE; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + /* Error correction for overlaying rounded corners */ + if ($SkewFactor < .5) { + $SkewFactor = .5; + } + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ($Data["Abscissa"] == "") { + return PIE_NO_ABSCISSA; + } + + /* Try to find the data serie */ + $DataSerie = null; + foreach ($Data["Series"] as $SerieName => $SerieData) { + if ($SerieName != $Data["Abscissa"]) { + $DataSerie = $SerieName; + } + } + + /* Do we have data to compute? */ + if (!$DataSerie) { + return PIE_NO_DATASERIE; + } + + /* Remove unused data */ + list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ($SerieSum == 0) { + return PIE_SUMISNULL; + } + + /* Dump the real number of data to draw */ + $Values = []; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if ($Value != 0) { + $Values[] = $Value; + } + } + + /* Compute the wasted angular space between series */ + if (count($Values) == 1) { + $WastedAngular = 0; + } else { + $WastedAngular = count($Values) * $DataGapAngle; + } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ($this->pChartObject->Shadow) { + $this->pChartObject->Shadow = false; + } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + $ID = count($Values) - 1; + $Values = array_reverse($Values); + $Slice = 0; + $Slices = []; + $SliceColors = []; + $Visible = []; + $SliceAngle = []; + foreach ($Values as $Key => $Value) { + if (!isset($Palette[$ID]["R"])) { + $Color = $this->pChartObject->getRandomColor(); + $Palette[$ID] = $Color; + $this->pDataObject->savePalette($ID, $Color); + } + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + + $SliceColors[$Slice] = $Settings; + + $StartAngle = $Offset; + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + if ($StartAngle > 180) { + $Visible[$Slice]["Start"] = true; + } else { + $Visible[$Slice]["Start"] = true; + } + if ($EndAngle < 180) { + $Visible[$Slice]["End"] = false; + } else { + $Visible[$Slice]["End"] = true; + } + + if ($DataGapAngle == 0) { + $X0 = $X; + $Y0 = $Y; + } else { + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X; + $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y; + } + $Slices[$Slice][] = $X0; + $Slices[$Slice][] = $Y0; + $SliceAngle[$Slice][] = 0; + + for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) { + $Xc = cos(($i - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + + if (($SecondPass || $RestoreShadow) && ($i < 90)) { + $Yc++; + } + if (($SecondPass || $RestoreShadow) && ($i > 90 && $i < 180)) { + $Xc++; + } + if (($SecondPass || $RestoreShadow) && ($i > 180 && $i < 270)) { + $Xc++; + } + if (($SecondPass || $RestoreShadow) && ($i >= 270)) { + $Xc++; + $Yc++; + } + + $Slices[$Slice][] = $Xc; + $Slices[$Slice][] = $Yc; + $SliceAngle[$Slice][] = $i; + } + + $Offset = $i - $DataGapAngle; + $ID--; + $Slice++; + } + + /* Draw the bottom shadow if needed */ + if ($RestoreShadow && ($this->pChartObject->ShadowX != 0 || $this->pChartObject->ShadowY != 0)) { + foreach ($Slices as $SliceID => $Plots) { + $ShadowPie = []; + for ($i = 0; $i < count($Plots); $i = $i + 2) { + $ShadowPie[] = $Plots[$i] + $this->pChartObject->ShadowX; + $ShadowPie[] = $Plots[$i + 1] + $this->pChartObject->ShadowY; + } + + $Settings = [ + "R" => $this->pChartObject->ShadowR, + "G" => $this->pChartObject->ShadowG, + "B" => $this->pChartObject->ShadowB, + "Alpha" => $this->pChartObject->Shadowa, + "NoBorder" => true + ]; + $this->pChartObject->drawPolygon($ShadowPie, $Settings); + } + + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + foreach ($Values as $Key => $Value) { + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) { + $Xc = cos(($i - 90) * PI / 180) * $Radius + $X + $this->pChartObject->ShadowX; + $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y + $this->pChartObject->ShadowY; + + $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings); + } + + $Offset = $i - $DataGapAngle; + $ID--; + } + } + + /* Draw the bottom pie splice */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $this->pChartObject->drawPolygon($Plots, $Settings); + + if ($SecondPass) { + $Settings = $SliceColors[$SliceID]; + if ($Border) { + $Settings["R"] += 30; + $Settings["G"] += 30; + $Settings["B"] += 30; + } + /* Empty error handling */ + if (isset($SliceAngle[$SliceID][1])) { + $Angle = $SliceAngle[$SliceID][1]; + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings); + + $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1]; + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings); + } + } + } + + /* Draw the two vertical edges */ + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["R"] += 10; + $Settings["G"] += 10; + $Settings["B"] += 10; + $Settings["NoBorder"] = true; + /* Empty error handling */ + if ($Visible[$SliceID]["Start"] && isset($Plots[2])) { + $this->pChartObject->drawLine( + $Plots[2], + $Plots[3], + $Plots[2], + $Plots[3] - $SliceHeight, + ["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]] + ); + $Border = []; + $Border[] = $Plots[0]; + $Border[] = $Plots[1]; + $Border[] = $Plots[0]; + $Border[] = $Plots[1] - $SliceHeight; + $Border[] = $Plots[2]; + $Border[] = $Plots[3] - $SliceHeight; + $Border[] = $Plots[2]; + $Border[] = $Plots[3]; + $this->pChartObject->drawPolygon($Border, $Settings); + } + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["R"] += 10; + $Settings["G"] += 10; + $Settings["B"] += 10; + $Settings["NoBorder"] = true; + if ($Visible[$SliceID]["End"]) { + $this->pChartObject->drawLine( + $Plots[count($Plots) - 2], + $Plots[count($Plots) - 1], + $Plots[count($Plots) - 2], + $Plots[count($Plots) - 1] - $SliceHeight, + ["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]] + ); + + $Border = []; + $Border[] = $Plots[0]; + $Border[] = $Plots[1]; + $Border[] = $Plots[0]; + $Border[] = $Plots[1] - $SliceHeight; + $Border[] = $Plots[count($Plots) - 2]; + $Border[] = $Plots[count($Plots) - 1] - $SliceHeight; + $Border[] = $Plots[count($Plots) - 2]; + $Border[] = $Plots[count($Plots) - 1]; + $this->pChartObject->drawPolygon($Border, $Settings); + } + } + + /* Draw the rounded edges */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["R"] += 10; + $Settings["G"] += 10; + $Settings["B"] += 10; + $Settings["NoBorder"] = true; + + for ($j = 2; $j < count($Plots) - 2; $j = $j + 2) { + $Angle = $SliceAngle[$SliceID][$j / 2]; + if ($Angle < 270 && $Angle > 90) { + $Border = []; + $Border[] = $Plots[$j]; + $Border[] = $Plots[$j + 1]; + $Border[] = $Plots[$j + 2]; + $Border[] = $Plots[$j + 3]; + $Border[] = $Plots[$j + 2]; + $Border[] = $Plots[$j + 3] - $SliceHeight; + $Border[] = $Plots[$j]; + $Border[] = $Plots[$j + 1] - $SliceHeight; + $this->pChartObject->drawPolygon($Border, $Settings); + } + } + + if ($SecondPass) { + $Settings = $SliceColors[$SliceID]; + if (count($Border)) { + $Settings["R"] += 30; + $Settings["G"] += 30; + $Settings["B"] += 30; + } + + /* Empty error handling */ + if (isset($SliceAngle[$SliceID][1])) { + $Angle = $SliceAngle[$SliceID][1]; + if ($Angle < 270 && $Angle > 90) { + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings); + } + } + + $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1]; + if ($Angle < 270 && $Angle > 90) { + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings); + } + + if (isset($SliceAngle[$SliceID][1]) + && $SliceAngle[$SliceID][1] > 270 + && $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 270 + ) { + $Xc = cos((270 - 90) * PI / 180) * $Radius + $X; + $Yc = sin((270 - 90) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings); + } + + if (isset($SliceAngle[$SliceID][1]) + && $SliceAngle[$SliceID][1] > 90 + && $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 90 + ) { + $Xc = cos((0) * PI / 180) * $Radius + $X; + $Yc = sin((0) * PI / 180) * $Radius * $SkewFactor + $Y; + $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings); + } + } + } + + /* Draw the top splice */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["R"] += 20; + $Settings["G"] += 20; + $Settings["B"] += 20; + + $Top = []; + for ($j = 0; $j < count($Plots); $j = $j + 2) { + $Top[] = $Plots[$j]; + $Top[] = $Plots[$j + 1] - $SliceHeight; + } + $this->pChartObject->drawPolygon($Top, $Settings); + + if ($RecordImageMap && !$Shadow) { + $this->pChartObject->addToImageMap( + "POLY", + $this->arraySerialize($Top), + $this->pChartObject->toHTMLColor( + $Settings["R"], + $Settings["G"], + $Settings["B"] + ), + $Data["Series"][$Data["Abscissa"]]["Data"][count($Slices) - $SliceID - 1], + $Values[$SliceID] + ); + } + } + + + /* Second pass to smooth the angles */ + if ($SecondPass) { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + $ID = count($Values) - 1; + foreach ($Values as $Key => $Value) { + $FirstPoint = true; + if ($Shadow) { + $Settings = [ + "R" => $this->pChartObject->ShadowR, + "G" => $this->pChartObject->ShadowG, + "B" => $this->pChartObject->ShadowB, + "Alpha" => $this->pChartObject->Shadowa + ]; + } else { + if ($Border) { + $Settings = [ + "R" => $Palette[$ID]["R"] + 30, + "G" => $Palette[$ID]["G"] + 30, + "B" => $Palette[$ID]["B"] + 30, + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } + } + + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + if ($DataGapAngle == 0) { + $X0 = $X; + $Y0 = $Y - $SliceHeight; + } else { + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X; + $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y - $SliceHeight; + } + $Plots[] = $X0; + $Plots[] = $Y0; + + for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) { + $Xc = cos(($i - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight; + + if ($FirstPoint) { + $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings); + $FirstPoint = false; + } + + $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings); + if ($i < 270 && $i > 90) { + $this->pChartObject->drawAntialiasPixel($Xc, $Yc + $SliceHeight, $Settings); + } + } + $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings); + + $Offset = $i - $DataGapAngle; + $ID--; + } + } + + if ($WriteValues != null) { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + $ID = count($Values) - 1; + $Settings = [ + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "R" => $ValueR, + "G" => $ValueG, + "B" => $ValueB, + "Alpha" => $ValueAlpha + ]; + foreach ($Values as $Key => $Value) { + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + + if ($ValuePosition == PIE_VALUE_OUTSIDE) { + $Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X; + $Yc = sin(($Angle - 90) * PI / 180) + * (($Radius * $SkewFactor) + $ValuePadding) + + $Y - $SliceHeight + ; + } else { + $Xc = cos(($Angle - 90) * PI / 180) * ($Radius) / 2 + $X; + $Yc = sin(($Angle - 90) * PI / 180) * ($Radius * $SkewFactor) / 2 + $Y - $SliceHeight; + } + + if ($WriteValues == PIE_VALUE_PERCENTAGE) { + $Display = round((100 / $SerieSum) * $Value, $Precision) . "%"; + } elseif ($WriteValues == PIE_VALUE_NATURAL) { + $Display = $Value . $ValueSuffix; + } + + $this->pChartObject->drawText($Xc, $Yc, $Display, $Settings); + + $Offset = $EndAngle - $DataGapAngle; + $ID--; + } + } + + if ($DrawLabels) { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + $ID = count($Values) - 1; + foreach ($Values as $Key => $Value) { + if ($LabelColor == PIE_LABEL_COLOR_AUTO) { + $Settings = [ + "FillR" => $Palette[$ID]["R"], + "FillG" => $Palette[$ID]["G"], + "FillB" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "FillR" => $LabelR, + "FillG" => $LabelG, + "FillB" => $LabelB, + "Alpha" => $LabelAlpha + ]; + } + + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight; + + if (isset($Data["Series"][$Data["Abscissa"]]["Data"][$ID])) { + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$ID]; + + if ($LabelStacked) { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius, true); + } else { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false); + } + } + + $Offset = $EndAngle - $DataGapAngle; + $ID--; + } + } + + if ($DrawLabels && $LabelStacked) { + $this->writeShiftedLabels(); + } + + $this->pChartObject->Shadow = $RestoreShadow; + + return PIE_RENDERED; + } + + /** + * Draw the legend of pie chart + * @param int $X + * @param int $Y + * @param array $Format + * @return int + */ + public function drawPieLegend($X, $Y, array $Format = []) + { + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; + $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + $YStep = max($this->pChartObject->FontSize, $BoxSize) + 5; + $XStep = $BoxSize + 5; + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ($Data["Abscissa"] == "") { + return PIE_NO_ABSCISSA; + } + + $Boundaries = []; + $Boundaries["L"] = $X; + $Boundaries["T"] = $Y; + $Boundaries["R"] = 0; + $Boundaries["B"] = 0; + $vY = $Y; + $vX = $X; + foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) { + $BoxArray = $this->pChartObject->getTextBox( + $vX + $BoxSize + 4, + $vY + $BoxSize / 2, + $FontName, + $FontSize, + 0, + $Value + ); + + if ($Mode == LEGEND_VERTICAL) { + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2; + } + $vY = $vY + $YStep; + } elseif ($Mode == LEGEND_HORIZONTAL) { + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2; + } + $vX = $Boundaries["R"] + $XStep; + } + } + $vY = $vY - $YStep; + $vX = $vX - $XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ($Boundaries["B"] - ($vY + $BoxSize) < $TopOffset) { + $Boundaries["B"] = $vY + $BoxSize + $TopOffset; + } + + if ($Style == LEGEND_ROUND) { + $this->pChartObject->drawRoundedFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } elseif ($Style == LEGEND_BOX) { + $this->pChartObject->drawFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) { + $R = $Palette[$Key]["R"]; + $G = $Palette[$Key]["G"]; + $B = $Palette[$Key]["B"]; + + $this->pChartObject->drawFilledRectangle( + $X + 1, + $Y + 1, + $X + $BoxSize + 1, + $Y + $BoxSize + 1, + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20] + ); + $this->pChartObject->drawFilledRectangle( + $X, + $Y, + $X + $BoxSize, + $Y + $BoxSize, + ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20] + ); + if ($Mode == LEGEND_VERTICAL) { + $this->pChartObject->drawText( + $X + $BoxSize + 4, + $Y + $BoxSize / 2, + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT, + "FontName" => $FontName, + "FontSize" => $FontSize + ] + ); + $Y = $Y + $YStep; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $BoxArray = $this->pChartObject->drawText( + $X + $BoxSize + 4, + $Y + $BoxSize / 2, + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT, + "FontName" => $FontName, + "FontSize" => $FontSize + ] + ); + $X = $BoxArray[1]["X"] + 2 + $XStep; + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + } + + /** + * Set the color of the specified slice + * @param mixed $SliceID + * @param array $Format + */ + public function setSliceColor($SliceID, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + $this->pDataObject->Palette[$SliceID]["R"] = $R; + $this->pDataObject->Palette[$SliceID]["G"] = $G; + $this->pDataObject->Palette[$SliceID]["B"] = $B; + $this->pDataObject->Palette[$SliceID]["Alpha"] = $Alpha; + } + + /** + * Internally used compute the label positions + * @param int $X + * @param int $Y + * @param string $Label + * @param int|float $Angle + * @param array $Settings + * @param boolean $Stacked + * @param int $Xc + * @param int $Yc + * @param int $Radius + * @param boolean $Reversed + */ + public function writePieLabel( + $X, + $Y, + $Label, + $Angle, + $Settings, + $Stacked, + $Xc = 0, + $Yc = 0, + $Radius = 0, + $Reversed = false + ) { + $LabelOffset = 30; + $FontName = $this->pChartObject->FontName; + $FontSize = $this->pChartObject->FontSize; + + if (!$Stacked) { + $Settings["Angle"] = 360 - $Angle; + $Settings["Length"] = 25; + $Settings["Size"] = 8; + + $this->pChartObject->drawArrowLabel($X, $Y, " " . $Label . " ", $Settings); + } else { + $X2 = cos(deg2rad($Angle - 90)) * 20 + $X; + $Y2 = sin(deg2rad($Angle - 90)) * 20 + $Y; + + $TxtPos = $this->pChartObject->getTextBox($X, $Y, $FontName, $FontSize, 0, $Label); + $Height = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; + $YTop = $Y2 - $Height / 2 - 2; + $YBottom = $Y2 + $Height / 2 + 2; + + if (count($this->LabelPos)) { + $Done = false; + foreach ($this->LabelPos as $Key => $Settings) { + if (!$Done) { + $yTopAboveTopBelowBottom = ($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]); + $yBottomAboveTopBelowBottom = ($YBottom >= $Settings["YTop"] + && $YBottom <= $Settings["YBottom"] + ); + + if ($Angle <= 90 + && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom) + ) { + $this->shift(0, 180, -($Height + 2), $Reversed); + $Done = true; + } + if ($Angle > 90 + && $Angle <= 180 + && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom) + ) { + $this->shift(0, 180, -($Height + 2), $Reversed); + $Done = true; + } + if ($Angle > 180 + && $Angle <= 270 + && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom) + ) { + $this->shift(180, 360, ($Height + 2), $Reversed); + $Done = true; + } + if ($Angle > 270 + && $Angle <= 360 + && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom) + ) { + $this->shift(180, 360, ($Height + 2), $Reversed); + $Done = true; + } + } + } + } + + $LabelSettings = [ + "YTop" => $YTop, + "YBottom" => $YBottom, + "Label" => $Label, + "Angle" => $Angle, + "X1" => $X, + "Y1" => $Y, + "X2" => $X2, + "Y2" => $Y2 + ]; + if ($Angle <= 180) { + $LabelSettings["X3"] = $Xc + $Radius + $LabelOffset; + } + if ($Angle > 180) { + $LabelSettings["X3"] = $Xc - $Radius - $LabelOffset; + } + $this->LabelPos[] = $LabelSettings; + } + } + + /** + * Internally used to shift label positions + * @param int $StartAngle + * @param int $EndAngle + * @param int $Offset + * @param boolean $Reversed + */ + public function shift($StartAngle, $EndAngle, $Offset, $Reversed) + { + if ($Reversed) { + $Offset = -$Offset; + } + foreach ($this->LabelPos as $Key => $Settings) { + if ($Settings["Angle"] > $StartAngle && $Settings["Angle"] <= $EndAngle) { + $this->LabelPos[$Key]["YTop"] = $Settings["YTop"] + $Offset; + $this->LabelPos[$Key]["YBottom"] = $Settings["YBottom"] + $Offset; + $this->LabelPos[$Key]["Y2"] = $Settings["Y2"] + $Offset; + } + } + } + + /** + * Internally used to write the re-computed labels + * @return null|int + */ + public function writeShiftedLabels() + { + if (!count($this->LabelPos)) { + return 0; + } + foreach ($this->LabelPos as $Settings) { + $X1 = $Settings["X1"]; + $Y1 = $Settings["Y1"]; + $X2 = $Settings["X2"]; + $Y2 = $Settings["Y2"]; + $X3 = $Settings["X3"]; + $Angle = $Settings["Angle"]; + $Label = $Settings["Label"]; + + $this->pChartObject->drawArrow($X2, $Y2, $X1, $Y1, ["Size" => 8]); + if ($Angle <= 180) { + $this->pChartObject->drawLine($X2, $Y2, $X3, $Y2); + $this->pChartObject->drawText($X3 + 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLELEFT]); + } else { + $this->pChartObject->drawLine($X2, $Y2, $X3, $Y2); + $this->pChartObject->drawText($X3 - 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLERIGHT]); + } + } + } + + /** + * Draw a ring chart + * @param int $X + * @param int $Y + * @param array $Format + * @return int + */ + public function draw2DRing($X, $Y, array $Format = []) + { + $OuterRadius = isset($Format["Radius"]) ? $Format["Radius"] : 60; // For compatibility + $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : $OuterRadius; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30; + $Border = isset($Format["Border"]) ? $Format["Border"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null; //PIE_VALUE_PERCENTAGE + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 5; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ($Data["Abscissa"] == "") { + return PIE_NO_ABSCISSA; + } + + /* Try to find the data serie */ + $DataSerie = null; + foreach ($Data["Series"] as $SerieName => $SerieData) { + if ($SerieName != $Data["Abscissa"]) { + $DataSerie = $SerieName; + } + } + + /* Do we have data to compute? */ + if (!$DataSerie) { + return PIE_NO_DATASERIE; + } + + /* Remove unused data */ + list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ($SerieSum == 0) { + return PIE_SUMISNULL; + } + + /* Dump the real number of data to draw */ + $Values = []; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if ($Value != 0) { + $Values[] = $Value; + } + } + + /* Compute the wasted angular space between series */ + if (count($Values) == 1) { + $WastedAngular = 0; + } else { + $WastedAngular = 0; + } // count($Values) + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ($this->pChartObject->Shadow) { + $this->pChartObject->Shadow = false; + + $ShadowFormat = $Format; + $ShadowFormat["Shadow"] = true; + $this->draw2DRing( + $X + $this->pChartObject->ShadowX, + $Y + $this->pChartObject->ShadowY, + $ShadowFormat + ); + } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $OuterRadius); + $Offset = 0; + $ID = 0; + foreach ($Values as $Key => $Value) { + if ($Shadow) { + $Settings = [ + "R" => $this->pChartObject->ShadowR, + "G" => $this->pChartObject->ShadowG, + "B" => $this->pChartObject->ShadowB, + "Alpha" => $this->pChartObject->Shadowa + ]; + $BorderColor = $Settings; + } else { + if (!isset($Palette[$ID]["R"])) { + $Color = $this->pChartObject->getRandomColor(); + $Palette[$ID] = $Color; + $this->pDataObject->savePalette($ID, $Color); + } + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + + if ($Border) { + $BorderColor = [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha + ]; + } else { + $BorderColor = $Settings; + } + } + + $Plots = []; + $Boundaries = []; + $AAPixels = []; + $EndAngle = $Offset + ($Value * $ScaleFactor); + if ($EndAngle > 360) { + $EndAngle = 360; + } + for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) { + $Xc = cos(($i - 90) * PI / 180) * $OuterRadius + $X; + $Yc = sin(($i - 90) * PI / 180) * $OuterRadius + $Y; + + if (!isset($Boundaries[0]["X1"])) { + $Boundaries[0]["X1"] = $Xc; + $Boundaries[0]["Y1"] = $Yc; + } + $AAPixels[] = [$Xc, $Yc]; + + if ($i < 90) { + $Yc++; + } + if ($i > 180 && $i < 270) { + $Xc++; + } + if ($i >= 270) { + $Xc++; + $Yc++; + } + + $Plots[] = $Xc; + $Plots[] = $Yc; + } + $Boundaries[1]["X1"] = $Xc; + $Boundaries[1]["Y1"] = $Yc; + $Lasti = $EndAngle; + + for ($i = $EndAngle; $i >= $Offset; $i = $i - $Step) { + $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius - 1) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius - 1) + $Y; + + if (!isset($Boundaries[1]["X2"])) { + $Boundaries[1]["X2"] = $Xc; + $Boundaries[1]["Y2"] = $Yc; + } + $AAPixels[] = [$Xc, $Yc]; + + $Xc = cos(($i - 90) * PI / 180) * $InnerRadius + $X; + $Yc = sin(($i - 90) * PI / 180) * $InnerRadius + $Y; + + if ($i < 90) { + $Yc++; + } + if ($i > 180 && $i < 270) { + $Xc++; + } + if ($i >= 270) { + $Xc++; + $Yc++; + } + + $Plots[] = $Xc; + $Plots[] = $Yc; + } + $Boundaries[0]["X2"] = $Xc; + $Boundaries[0]["Y2"] = $Yc; + + /* Draw the polygon */ + $this->pChartObject->drawPolygon($Plots, $Settings); + if ($RecordImageMap && !$Shadow) { + $this->pChartObject->addToImageMap( + "POLY", + $this->arraySerialize($Plots), + $this->pChartObject->toHTMLColor( + $Palette[$ID]["R"], + $Palette[$ID]["G"], + $Palette[$ID]["B"] + ), + $Data["Series"][$Data["Abscissa"]]["Data"][$Key], + $Value + ); + } + + /* Smooth the edges using AA */ + foreach ($AAPixels as $Pos) { + $this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $BorderColor); + } + $this->pChartObject->drawLine( + $Boundaries[0]["X1"], + $Boundaries[0]["Y1"], + $Boundaries[0]["X2"], + $Boundaries[0]["Y2"], + $BorderColor + ); + $this->pChartObject->drawLine( + $Boundaries[1]["X1"], + $Boundaries[1]["Y1"], + $Boundaries[1]["X2"], + $Boundaries[1]["Y2"], + $BorderColor + ); + + if ($DrawLabels && !$Shadow) { + if ($LabelColor == PIE_LABEL_COLOR_AUTO) { + $Settings = [ + "FillR" => $Palette[$ID]["R"], + "FillG" => $Palette[$ID]["G"], + "FillB" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "FillR" => $LabelR, + "FillG" => $LabelG, + "FillB" => $LabelB, + "Alpha" => $LabelAlpha + ]; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $Xc = cos(($Angle - 90) * PI / 180) * $OuterRadius + $X; + $Yc = sin(($Angle - 90) * PI / 180) * $OuterRadius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ($LabelStacked) { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $OuterRadius); + } else { + $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false); + } + } + + $Offset = $Lasti; + $ID++; + } + + if ($DrawLabels && $LabelStacked) { + $this->writeShiftedLabels(); + } + + if ($WriteValues && !$Shadow) { + $Step = 360 / (2 * PI * $OuterRadius); + $Offset = 0; + foreach ($Values as $Key => $Value) { + $EndAngle = $Offset + ($Value * $ScaleFactor); + if ($EndAngle > 360) { + $EndAngle = 360; + } + + $Angle = $Offset + ($Value * $ScaleFactor) / 2; + if ($ValuePosition == PIE_VALUE_OUTSIDE) { + $Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $X; + $Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $Y; + if ($Angle >= 0 && $Angle <= 90) { + $Align = TEXT_ALIGN_BOTTOMLEFT; + } + if ($Angle > 90 && $Angle <= 180) { + $Align = TEXT_ALIGN_TOPLEFT; + } + if ($Angle > 180 && $Angle <= 270) { + $Align = TEXT_ALIGN_TOPRIGHT; + } + if ($Angle > 270) { + $Align = TEXT_ALIGN_BOTTOMRIGHT; + } + } else { + $Xc = cos(($Angle - 90) * PI / 180) + * (($OuterRadius - $InnerRadius) / 2 + $InnerRadius) + + $X + ; + $Yc = sin(($Angle - 90) * PI / 180) + * (($OuterRadius - $InnerRadius) / 2 + $InnerRadius) + + $Y + ; + $Align = TEXT_ALIGN_MIDDLEMIDDLE; + } + + if ($WriteValues == PIE_VALUE_PERCENTAGE) { + $Display = round((100 / $SerieSum) * $Value, $Precision) . "%"; + } elseif ($WriteValues == PIE_VALUE_NATURAL) { + $Display = $Value . $ValueSuffix; + } else { + $Display = ""; + } + $this->pChartObject->drawText( + $Xc, + $Yc, + $Display, + ["Align" => $Align, "R" => $ValueR, "G" => $ValueG, "B" => $ValueB] + ); + $Offset = $EndAngle; + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + + return PIE_RENDERED; + } + + /** + * Draw a 3D ring chart + * @param int $X + * @param int $Y + * @param array $Format + * @return int + */ + public function draw3DRing($X, $Y, array $Format = []) + { + $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : 100; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30; + $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .6; + $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 10; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 10; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 10; + $Border = isset($Format["Border"]) ? $Format["Border"] : false; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $Cf = isset($Format["Cf"]) ? $Format["Cf"] : 20; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : PIE_VALUE_NATURAL; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : $SliceHeight + 15; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + /* Error correction for overlaying rounded corners */ + if ($SkewFactor < .5) { + $SkewFactor = .5; + } + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ($Data["Abscissa"] == "") { + return PIE_NO_ABSCISSA; + } + + /* Try to find the data serie */ + $DataSerie = null; + foreach ($Data["Series"] as $SerieName => $SerieData) { + if ($SerieName != $Data["Abscissa"]) { + $DataSerie = $SerieName; + } + } + + /* Do we have data to compute? */ + if (!$DataSerie) { + return PIE_NO_DATASERIE; + } + + /* Remove unused data */ + list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ($SerieSum == 0) { + return PIE_SUMISNULL; + } + + /* Dump the real number of data to draw */ + $Values = []; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if ($Value != 0) { + $Values[] = $Value; + } + } + + /* Compute the wasted angular space between series */ + if (count($Values) == 1) { + $WastedAngular = 0; + } else { + $WastedAngular = count($Values) * $DataGapAngle; + } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ($this->pChartObject->Shadow) { + $this->pChartObject->Shadow = false; + } + + /* Draw the polygon ring elements */ + $Offset = 360; + $ID = count($Values) - 1; + $Values = array_reverse($Values); + $Slice = 0; + $Slices = []; + $SliceColors = []; + $Visible = []; + $SliceAngle = []; + foreach ($Values as $Key => $Value) { + if (!isset($Palette[$ID]["R"])) { + $Color = $this->pChartObject->getRandomColor(); + $Palette[$ID] = $Color; + $this->pDataObject->savePalette($ID, $Color); + } + $Settings = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + + $SliceColors[$Slice] = $Settings; + + $StartAngle = $Offset; + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + if ($StartAngle > 180) { + $Visible[$Slice]["Start"] = true; + } else { + $Visible[$Slice]["Start"] = true; + } + if ($EndAngle < 180) { + $Visible[$Slice]["End"] = false; + } else { + $Visible[$Slice]["End"] = true; + } + + $Step = (360 / (2 * PI * $OuterRadius)) / 2; + $OutX1 = VOID; + $OutY1 = VOID; + for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) { + $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) * $SkewFactor + $Y; + $Slices[$Slice]["AA"][] = [$Xc, $Yc]; + + $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) * $SkewFactor + $Y; + $Slices[$Slice]["AA"][] = [$Xc, $Yc]; + + $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y; + $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings); + + if ($OutX1 == VOID) { + $OutX1 = $Xc; + $OutY1 = $Yc; + } + + if ($i < 90) { + $Yc++; + } + if ($i > 90 && $i < 180) { + $Xc++; + } + if ($i > 180 && $i < 270) { + $Xc++; + } + if ($i >= 270) { + $Xc++; + $Yc++; + } + + $Slices[$Slice]["BottomPoly"][] = floor($Xc); + $Slices[$Slice]["BottomPoly"][] = floor($Yc); + $Slices[$Slice]["TopPoly"][] = floor($Xc); + $Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight; + $Slices[$Slice]["Angle"][] = $i; + } + $OutX2 = $Xc; + $OutY2 = $Yc; + + $Slices[$Slice]["Angle"][] = VOID; + $Lasti = $i; + + $Step = (360 / (2 * PI * $InnerRadius)) / 2; + $InX1 = VOID; + $InY1 = VOID; + for ($i = $EndAngle; $i <= $Offset; $i = $i + $Step) { + $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) * $SkewFactor + $Y; + $Slices[$Slice]["AA"][] = [$Xc, $Yc]; + + $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) + $X; + $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) * $SkewFactor + $Y; + $Slices[$Slice]["AA"][] = [$Xc, $Yc]; + + if ($InX1 == VOID) { + $InX1 = $Xc; + $InY1 = $Yc; + } + + if ($i < 90) { + $Yc++; + } + if ($i > 90 && $i < 180) { + $Xc++; + } + if ($i > 180 && $i < 270) { + $Xc++; + } + if ($i >= 270) { + $Xc++; + $Yc++; + } + + $Slices[$Slice]["BottomPoly"][] = floor($Xc); + $Slices[$Slice]["BottomPoly"][] = floor($Yc); + $Slices[$Slice]["TopPoly"][] = floor($Xc); + $Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight; + $Slices[$Slice]["Angle"][] = $i; + } + $InX2 = $Xc; + $InY2 = $Yc; + + $Slices[$Slice]["InX1"] = $InX1; + $Slices[$Slice]["InY1"] = $InY1; + $Slices[$Slice]["InX2"] = $InX2; + $Slices[$Slice]["InY2"] = $InY2; + $Slices[$Slice]["OutX1"] = $OutX1; + $Slices[$Slice]["OutY1"] = $OutY1; + $Slices[$Slice]["OutX2"] = $OutX2; + $Slices[$Slice]["OutY2"] = $OutY2; + + $Offset = $Lasti - $DataGapAngle; + $ID--; + $Slice++; + } + + /* Draw the bottom pie splice */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $this->pChartObject->drawPolygon($Plots["BottomPoly"], $Settings); + + foreach ($Plots["AA"] as $Key => $Pos) { + $this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $Settings); + } + $this->pChartObject->drawLine( + $Plots["InX1"], + $Plots["InY1"], + $Plots["OutX2"], + $Plots["OutY2"], + $Settings + ); + $this->pChartObject->drawLine( + $Plots["InX2"], + $Plots["InY2"], + $Plots["OutX1"], + $Plots["OutY1"], + $Settings + ); + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + + /* Draw the vertical edges (semi-visible) */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf; + $Settings["G"] = $Settings["G"] + $Cf; + $Settings["B"] = $Settings["B"] + $Cf; + + $StartAngle = $Plots["Angle"][0]; + foreach ($Plots["Angle"] as $Key => $Angle) { + if ($Angle == VOID) { + $EndAngle = $Plots["Angle"][$Key - 1]; + } + } + + if ($StartAngle >= 270 || $StartAngle <= 90) { + $this->pChartObject->drawLine( + $Plots["OutX1"], + $Plots["OutY1"], + $Plots["OutX1"], + $Plots["OutY1"] - $SliceHeight, + $Settings + ); + } + if ($StartAngle >= 270 || $StartAngle <= 90) { + $this->pChartObject->drawLine( + $Plots["OutX2"], + $Plots["OutY2"], + $Plots["OutX2"], + $Plots["OutY2"] - $SliceHeight, + $Settings + ); + } + $this->pChartObject->drawLine( + $Plots["InX1"], + $Plots["InY1"], + $Plots["InX1"], + $Plots["InY1"] - $SliceHeight, + $Settings + ); + $this->pChartObject->drawLine( + $Plots["InX2"], + $Plots["InY2"], + $Plots["InX2"], + $Plots["InY2"] - $SliceHeight, + $Settings + ); + } + + /* Draw the inner vertical slices */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf; + $Settings["G"] = $Settings["G"] + $Cf; + $Settings["B"] = $Settings["B"] + $Cf; + + $Outer = true; + $Inner = false; + $InnerPlotsA = []; + $InnerPlotsB = []; + foreach ($Plots["Angle"] as $ID => $Angle) { + if ($Angle == VOID) { + $Outer = false; + $Inner = true; + } elseif ($Inner && ($Angle < 90 || $Angle > 270) && isset($Plots["BottomPoly"][$ID * 2])) { + $Xo = $Plots["BottomPoly"][$ID * 2]; + $Yo = $Plots["BottomPoly"][$ID * 2 + 1]; + + $InnerPlotsA[] = $Xo; + $InnerPlotsA[] = $Yo; + $InnerPlotsB[] = $Xo; + $InnerPlotsB[] = $Yo - $SliceHeight; + } + } + + if (count($InnerPlotsA)) { + $InnerPlots = array_merge($InnerPlotsA, $this->arrayReverse($InnerPlotsB)); + $this->pChartObject->drawPolygon($InnerPlots, $Settings); + } + } + + /* Draw the splice top and left poly */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf * 1.5; + $Settings["G"] = $Settings["G"] + $Cf * 1.5; + $Settings["B"] = $Settings["B"] + $Cf * 1.5; + + $StartAngle = $Plots["Angle"][0]; + foreach ($Plots["Angle"] as $Key => $Angle) { + if ($Angle == VOID) { + $EndAngle = $Plots["Angle"][$Key - 1]; + } + } + + if ($StartAngle < 180) { + $Points = []; + $Points[] = $Plots["InX2"]; + $Points[] = $Plots["InY2"]; + $Points[] = $Plots["InX2"]; + $Points[] = $Plots["InY2"] - $SliceHeight; + $Points[] = $Plots["OutX1"]; + $Points[] = $Plots["OutY1"] - $SliceHeight; + $Points[] = $Plots["OutX1"]; + $Points[] = $Plots["OutY1"]; + + $this->pChartObject->drawPolygon($Points, $Settings); + } + + if ($EndAngle > 180) { + $Points = []; + $Points[] = $Plots["InX1"]; + $Points[] = $Plots["InY1"]; + $Points[] = $Plots["InX1"]; + $Points[] = $Plots["InY1"] - $SliceHeight; + $Points[] = $Plots["OutX2"]; + $Points[] = $Plots["OutY2"] - $SliceHeight; + $Points[] = $Plots["OutX2"]; + $Points[] = $Plots["OutY2"]; + + $this->pChartObject->drawPolygon($Points, $Settings); + } + } + + + /* Draw the vertical edges (visible) */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf; + $Settings["G"] = $Settings["G"] + $Cf; + $Settings["B"] = $Settings["B"] + $Cf; + + $StartAngle = $Plots["Angle"][0]; + foreach ($Plots["Angle"] as $Key => $Angle) { + if ($Angle == VOID) { + $EndAngle = $Plots["Angle"][$Key - 1]; + } + } + + if ($StartAngle <= 270 && $StartAngle >= 90) { + $this->pChartObject->drawLine( + $Plots["OutX1"], + $Plots["OutY1"], + $Plots["OutX1"], + $Plots["OutY1"] - $SliceHeight, + $Settings + ); + } + if ($EndAngle <= 270 && $EndAngle >= 90) { + $this->pChartObject->drawLine( + $Plots["OutX2"], + $Plots["OutY2"], + $Plots["OutX2"], + $Plots["OutY2"] - $SliceHeight, + $Settings + ); + } + } + + + /* Draw the outer vertical slices */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf; + $Settings["G"] = $Settings["G"] + $Cf; + $Settings["B"] = $Settings["B"] + $Cf; + + $Outer = true; + $Inner = false; + $OuterPlotsA = []; + $OuterPlotsB = []; + $InnerPlotsA = []; + $InnerPlotsB = []; + foreach ($Plots["Angle"] as $ID => $Angle) { + if ($Angle == VOID) { + $Outer = false; + $Inner = true; + } elseif ($Outer && ($Angle > 90 && $Angle < 270) && isset($Plots["BottomPoly"][$ID * 2])) { + $Xo = $Plots["BottomPoly"][$ID * 2]; + $Yo = $Plots["BottomPoly"][$ID * 2 + 1]; + + $OuterPlotsA[] = $Xo; + $OuterPlotsA[] = $Yo; + $OuterPlotsB[] = $Xo; + $OuterPlotsB[] = $Yo - $SliceHeight; + } + } + if (count($OuterPlotsA)) { + $OuterPlots = array_merge($OuterPlotsA, $this->arrayReverse($OuterPlotsB)); + $this->pChartObject->drawPolygon($OuterPlots, $Settings); + } + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + + /* Draw the top pie splice */ + foreach ($Slices as $SliceID => $Plots) { + $Settings = $SliceColors[$SliceID]; + $Settings["NoBorder"] = true; + $Settings["R"] = $Settings["R"] + $Cf * 2; + $Settings["G"] = $Settings["G"] + $Cf * 2; + $Settings["B"] = $Settings["B"] + $Cf * 2; + + $this->pChartObject->drawPolygon($Plots["TopPoly"], $Settings); + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "POLY", + $this->arraySerialize($Plots["TopPoly"]), + $this->pChartObject->toHTMLColor( + $Settings["R"], + $Settings["G"], + $Settings["B"] + ), + $Data["Series"][$Data["Abscissa"]]["Data"][$SliceID], + $Data["Series"][$DataSerie]["Data"][count($Slices) - $SliceID - 1] + ); + } + + foreach ($Plots["AA"] as $Key => $Pos) { + $this->pChartObject->drawAntialiasPixel( + $Pos[0], + $Pos[1] - $SliceHeight, + $Settings + ); + } + $this->pChartObject->drawLine( + $Plots["InX1"], + $Plots["InY1"] - $SliceHeight, + $Plots["OutX2"], + $Plots["OutY2"] - $SliceHeight, + $Settings + ); + $this->pChartObject->drawLine( + $Plots["InX2"], + $Plots["InY2"] - $SliceHeight, + $Plots["OutX1"], + $Plots["OutY1"] - $SliceHeight, + $Settings + ); + } + + if ($DrawLabels) { + $Offset = 360; + foreach ($Values as $Key => $Value) { + $StartAngle = $Offset; + $EndAngle = $Offset - ($Value * $ScaleFactor); + if ($EndAngle < 0) { + $EndAngle = 0; + } + + if ($LabelColor == PIE_LABEL_COLOR_AUTO) { + $Settings = [ + "FillR" => $Palette[$ID]["R"], + "FillG" => $Palette[$ID]["G"], + "FillB" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } else { + $Settings = [ + "FillR" => $LabelR, + "FillG" => $LabelG, + "FillB" => $LabelB, + "Alpha" => $LabelAlpha + ]; + } + + $Angle = ($EndAngle - $Offset) / 2 + $Offset; + $Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X; + $Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y; + + $Label = ""; + if ($WriteValues == PIE_VALUE_PERCENTAGE) { + $Label = round((100 / $SerieSum) * $Value, $Precision) . "%"; + } elseif ($WriteValues == PIE_VALUE_NATURAL) { + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + } + + if ($LabelStacked) { + $this->writePieLabel( + $Xc, + $Yc - $SliceHeight, + $Label, + $Angle, + $Settings, + true, + $X, + $Y, + $OuterRadius + ); + } else { + $this->writePieLabel($Xc, $Yc - $SliceHeight, $Label, $Angle, $Settings, false); + } + $Offset = $EndAngle - $DataGapAngle; + $ID--; + $Slice++; + } + } + if ($DrawLabels && $LabelStacked) { + $this->writeShiftedLabels(); + } + + $this->pChartObject->Shadow = $RestoreShadow; + + return PIE_RENDERED; + } + + /** + * Serialize an array + * @param array $Data + * @return string + */ + public function arraySerialize(array $Data) + { + $Result = ""; + foreach ($Data as $Value) { + if ($Result == "") { + $Result = floor($Value); + } else { + $Result = $Result . "," . floor($Value); + } + } + + return $Result; + } + + /** + * Reverse an array + * @param array $Plots + * @return array + */ + public function arrayReverse(array $Plots) + { + $Result = []; + + for ($i = count($Plots) - 1; $i >= 0; $i = $i - 2) { + $Result[] = $Plots[$i - 1]; + $Result[] = $Plots[$i]; + } + + return $Result; + } + + /** + * Remove unused series & values + * @param array $Data + * @param array $Palette + * @param string $DataSerie + * @param string $AbscissaSerie + * @return array + */ + public function clean0Values(array $Data, array $Palette, $DataSerie, $AbscissaSerie) + { + $NewPalette = []; + $NewData = []; + $NewAbscissa = []; + + /* Remove unused series */ + foreach (array_keys($Data["Series"]) as $SerieName) { + if ($SerieName != $DataSerie && $SerieName != $AbscissaSerie) { + unset($Data["Series"][$SerieName]); + } + } + + /* Remove null values */ + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if ($Value != 0) { + $NewData[] = $Value; + $NewAbscissa[] = $Data["Series"][$AbscissaSerie]["Data"][$Key]; + if (isset($Palette[$Key])) { + $NewPalette[] = $Palette[$Key]; + } + } + } + $Data["Series"][$DataSerie]["Data"] = $NewData; + $Data["Series"][$AbscissaSerie]["Data"] = $NewAbscissa; + + return [$Data, $NewPalette]; + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Radar.php b/vendor/szymach/c-pchart/src/Chart/Radar.php new file mode 100644 index 0000000..63e7552 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Radar.php @@ -0,0 +1,1074 @@ +pChartObject = $Object; + + $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; + $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; + $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; + $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; + $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0; + $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; + $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; + $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; + $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; + $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName; + $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; + $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; + $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName + ; + $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize + ; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; + $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; + $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; + $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; + $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; + $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; + $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; + $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; + $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; + $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; + $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; + $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR; + $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; + $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; + $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1; + $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : false; + $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; + $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; + $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; + $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; + $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; + $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; + $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; + $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; + $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; + $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; + $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : true; + $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; + $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40; + $FontSize = $Object->FontSize; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + /* Cancel default tick length if ticks not enabled */ + if ($DrawTicks == false) { + $TicksLength = 0; + } + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + /* Catch the number of required axis */ + $LabelSerie = $Data["Abscissa"]; + if ($LabelSerie) { + $Points = count($Data["Series"][$LabelSerie]["Data"]); + } else { + $Points = 0; + foreach ($Data["Series"] as $SerieName => $DataArray) { + if (count($DataArray["Data"]) > $Points) { + $Points = count($DataArray["Data"]); + } + } + } + + /* Draw the axis */ + $CenterX = ($X2 - $X1) / 2 + $X1; + $CenterY = ($Y2 - $Y1) / 2 + $Y1; + + $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); + if ($WriteLabels) { + $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; + } + /* Determine the scale if set to automatic */ + if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { + if ($FixedMax != VOID) { + $Max = $FixedMax; + } else { + $Max = 0; + foreach ($Data["Series"] as $SerieName => $DataArray) { + if ($SerieName != $LabelSerie) { + if (max($DataArray["Data"]) > $Max) { + $Max = max($DataArray["Data"]); + } + } + } + } + $MaxSegments = $EdgeHeight / 20; + $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); + + $Segments = $Scale["Rows"]; + $SegmentHeight = $Scale["RowHeight"]; + } + + if ($LabelMiddle && $SkipLabels == 1) { + $Axisoffset = (360 / $Points) / 2; + } elseif ($LabelMiddle && $SkipLabels != 1) { + $Axisoffset = (360 / ($Points / $SkipLabels)) / 2; + } elseif (!$LabelMiddle) { + $Axisoffset = 0; + } + + /* Background processing */ + if ($DrawBackground) { + $RestoreShadow = $Object->Shadow; + $Object->Shadow = false; + + if ($BackgroundGradient == null) { + if ($Layout == RADAR_LAYOUT_STAR) { + $Color = [ + "R" => $BackgroundR, + "G" => $BackgroundG, + "B" => $BackgroundB, + "Alpha" => $BackgroundAlpha + ]; + $PointArray = []; + for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { + $PointArray[] = cos(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterX; + $PointArray[] = sin(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterY; + } + $Object->drawPolygon($PointArray, $Color); + } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { + $Color = [ + "R" => $BackgroundR, + "G" => $BackgroundG, + "B" => $BackgroundB, + "Alpha" => $BackgroundAlpha + ]; + $Object->drawFilledCircle( + $CenterX, + $CenterY, + $EdgeHeight, + $Color + ); + } + } else { + $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; + $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; + $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; + $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) + / $Segments + ; + + if ($Layout == RADAR_LAYOUT_STAR) { + for ($j = $Segments; $j >= 1; $j--) { + $Color = [ + "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, + "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, + "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, + "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j + ]; + $PointArray = []; + + for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { + $PointArray[] = cos(deg2rad($i + $AxisRotation)) + * ($EdgeHeight / $Segments) * $j + $CenterX + ; + $PointArray[] = sin(deg2rad($i + $AxisRotation)) + * ($EdgeHeight / $Segments) * $j + $CenterY + ; + } + $Object->drawPolygon($PointArray, $Color); + } + } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { + for ($j = $Segments; $j >= 1; $j--) { + $Color = [ + "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, + "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, + "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, + "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j + ]; + $Object->drawFilledCircle( + $CenterX, + $CenterY, + ($EdgeHeight / $Segments) * $j, + $Color + ); + } + } + } + $Object->Shadow = $RestoreShadow; + } + + /* Axis to axis lines */ + $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; + $ColorDotted = [ + "R" => $AxisR, + "G" => $AxisG, + "B" => $AxisB, + "Alpha" => $AxisAlpha * .8, + "Ticks" => 2 + ]; + if ($Layout == RADAR_LAYOUT_STAR) { + for ($j = 1; $j <= $Segments; $j++) { + for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { + $EdgeX1 = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; + $EdgeY1 = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; + $EdgeX2 = cos(deg2rad($i + $AxisRotation + (360 / $Points))) + * ($EdgeHeight / $Segments) * $j + $CenterX + ; + $EdgeY2 = sin(deg2rad($i + $AxisRotation + (360 / $Points))) + * ($EdgeHeight / $Segments) * $j + $CenterY + ; + + $Object->drawLine($EdgeX1, $EdgeY1, $EdgeX2, $EdgeY2, $Color); + } + } + } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { + for ($j = 1; $j <= $Segments; $j++) { + $Radius = ($EdgeHeight / $Segments) * $j; + $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); + } + } + + if ($DrawAxisValues) { + if ($LabelsBackground) { + $Options = [ + "DrawBox" => true, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "BoxR" => $LabelsBGR, + "BoxG" => $LabelsBGG, + "BoxB" => $LabelsBGB, + "BoxAlpha" => $LabelsBGAlpha + ]; + } else { + $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; + } + if ($AxisBoxRounded) { + $Options["BoxRounded"] = true; + } + + $Options["FontName"] = $AxisFontName; + $Options["FontSize"] = $AxisFontSize; + + $Angle = 360 / ($Points * 2); + for ($j = 1; $j <= $Segments; $j++) { + $Label = $j * $SegmentHeight; + + if ($Layout == RADAR_LAYOUT_CIRCLE) { + $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; + $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; + } elseif ($Layout == RADAR_LAYOUT_STAR) { + $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; + $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; + $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) + * $j + $CenterX + ; + $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) + * $j + $CenterY + ; + + $EdgeX1 = ($EdgeX2 - $EdgeX1) / 2 + $EdgeX1; + $EdgeY1 = ($EdgeY2 - $EdgeY1) / 2 + $EdgeY1; + } + + $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); + } + } + + /* Axis lines */ + $ID = 0; + for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { + $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; + $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; + + if ($ID % $SkipLabels == 0) { + $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); + } else { + $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $ColorDotted); + } + + if ($WriteLabels) { + $LabelX = cos(deg2rad($i + $AxisRotation + $Axisoffset)) + * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX + ; + $LabelY = sin(deg2rad($i + $AxisRotation + $Axisoffset)) + * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY + ; + + if ($LabelSerie) { + $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID]) + ? $Data["Series"][$LabelSerie]["Data"][$ID] : "" + ; + } else { + $Label = $ID; + } + + if ($ID % $SkipLabels == 0) { + if ($LabelPos == RADAR_LABELS_ROTATED) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + [ + "Angle" => (360 - ($i + $AxisRotation + $Axisoffset)) - 90, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } else { + if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMLEFT] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPLEFT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMRIGHT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_MIDDLERIGHT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPRIGHT] + ); + } + if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + } + } + } + } + $ID++; + } + + /* Compute the plots position */ + $ID = 0; + $Plot = []; + foreach ($Data["Series"] as $SerieName => $DataS) { + if ($SerieName != $LabelSerie) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + foreach ($DataS["Data"] as $Key => $Value) { + $Angle = (360 / $Points) * $Key; + $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; + + $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; + $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; + + $Plot[$ID][] = [$X, $Y, $Value]; + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), + $this->pChartObject->toHTMLColor( + $Palette[$ID]["R"], + $Palette[$ID]["G"], + $Palette[$ID]["B"] + ), + $DataS["Description"], + $Data["Series"][$LabelSerie]["Data"][$Key] . " = " . $Value + ); + } + } + $ID++; + } + } + + /* Draw all that stuff! */ + foreach ($Plot as $ID => $Points) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + + /* Draw the polygons */ + if ($DrawPoly) { + if ($PolyAlpha != null) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $PolyAlpha, + "Surrounding" => $PointSurrounding + ]; + } + $PointsArray = []; + for ($i = 0; $i < count($Points); $i++) { + $PointsArray[] = $Points[$i][0]; + $PointsArray[] = $Points[$i][1]; + } + $Object->drawPolygon($PointsArray, $Color); + } + + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + + /* Bubble and labels settings */ + $TextSettings = [ + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontName" => $ValueFontName, + "FontSize" => $ValueFontSize, + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"] + ]; + $InnerColor = [ + "R" => $InnerBubbleR, + "G" => $InnerBubbleG, + "B" => $InnerBubbleB, + "Alpha" => $InnerBubbleAlpha + ]; + if ($OuterBubbleR != VOID) { + $OuterColor = [ + "R" => $OuterBubbleR, + "G" => $OuterBubbleG, + "B" => $OuterBubbleB, + "Alpha" => $OuterBubbleAlpha + ]; + } else { + $OuterColor = [ + "R" => $Palette[$ID]["R"] + 20, + "G" => $Palette[$ID]["G"] + 20, + "B" => $Palette[$ID]["B"] + 20, + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } + /* Loop to the starting points if asked */ + if ($LineLoopStart && $DrawLines) { + $Object->drawLine( + $Points[count($Points) - 1][0], + $Points[count($Points) - 1][1], + $Points[0][0], + $Points[0][1], + $Color + ); + } + + /* Draw the lines & points */ + for ($i = 0; $i < count($Points); $i++) { + if ($DrawLines && $i < count($Points) - 1) { + $Object->drawLine( + $Points[$i][0], + $Points[$i][1], + $Points[$i + 1][0], + $Points[$i + 1][1], + $Color + ); + } + if ($DrawPoints) { + $Object->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $PointRadius, + $Color + ); + } + if ($WriteValuesInBubble && $WriteValues) { + $TxtPos = $this->pChartObject->getTextBox( + $Points[$i][0], + $Points[$i][1], + $ValueFontName, + $ValueFontSize, + 0, + $Points[$i][2] + ); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); + + $this->pChartObject->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $Radius + $OuterBubbleRadius, + $OuterColor + ); + $this->pChartObject->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $Radius, + $InnerColor + ); + } + + if ($WriteValues) { + $this->pChartObject->drawText( + $Points[$i][0] - 1, + $Points[$i][1] - 1, + $Points[$i][2], + $TextSettings + ); + } + } + } + } + + /** + * Draw a radar chart + * @param Image $Object + * @param Data $Values + * @param array $Format + */ + public function drawPolar(Image $Object, Data $Values, array $Format = []) + { + $this->pChartObject = $Object; + + $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; + $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; + $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; + $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; + $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90; + $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; + $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; + $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; + $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; + $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; + $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; + $ValueFontName = isset($Format["ValueFontName"]) + ? $Format["ValueFontName"] : $this->pChartObject->FontName + ; + $ValueFontSize = isset($Format["ValueFontSize"]) + ? $Format["ValueFontSize"] : $this->pChartObject->FontSize + ; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; + $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; + $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; + $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; + $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; + $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; + $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; + $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; + $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; + $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; + $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; + $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20; + $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; + $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; + $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; + $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; + $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; + $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; + $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; + $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; + $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; + $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; + $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; + $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; + $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : false; + $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; + $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : null; + $FontSize = $Object->FontSize; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + if ($AxisBoxRounded) { + $DrawAxisValues = true; + } + + /* Cancel default tick length if ticks not enabled */ + if ($DrawTicks == false) { + $TicksLength = 0; + } + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + /* Catch the number of required axis */ + $LabelSerie = $Data["Abscissa"]; + if ($LabelSerie) { + $Points = count($Data["Series"][$LabelSerie]["Data"]); + } else { + $Points = 0; + foreach ($Data["Series"] as $SerieName => $DataArray) { + if (count($DataArray["Data"]) > $Points) { + $Points = count($DataArray["Data"]); + } + } + } + + /* Draw the axis */ + $CenterX = ($X2 - $X1) / 2 + $X1; + $CenterY = ($Y2 - $Y1) / 2 + $Y1; + + $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); + if ($WriteLabels) { + $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; + } + + /* Determine the scale if set to automatic */ + if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { + if ($FixedMax != VOID) { + $Max = $FixedMax; + } else { + $Max = 0; + foreach ($Data["Series"] as $SerieName => $DataArray) { + if ($SerieName != $LabelSerie) { + if (max($DataArray["Data"]) > $Max) { + $Max = max($DataArray["Data"]); + } + } + } + } + $MaxSegments = $EdgeHeight / 20; + $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); + + $Segments = $Scale["Rows"]; + $SegmentHeight = $Scale["RowHeight"]; + } + + + /* Background processing */ + if ($DrawBackground) { + $RestoreShadow = $Object->Shadow; + $Object->Shadow = false; + + if ($BackgroundGradient == null) { + $Color = [ + "R" => $BackgroundR, + "G" => $BackgroundG, + "B" => $BackgroundB, + "Alpha" => $BackgroundAlpha + ]; + $Object->drawFilledCircle( + $CenterX, + $CenterY, + $EdgeHeight, + $Color + ); + } else { + $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; + $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; + $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; + $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) + / $Segments + ; + + for ($j = $Segments; $j >= 1; $j--) { + $Color = [ + "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, + "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, + "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, + "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j + ]; + $Object->drawFilledCircle( + $CenterX, + $CenterY, + ($EdgeHeight / $Segments) * $j, + $Color + ); + } + } + $Object->Shadow = $RestoreShadow; + } + + /* Axis to axis lines */ + $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; + for ($j = 1; $j <= $Segments; $j++) { + $Radius = ($EdgeHeight / $Segments) * $j; + $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); + } + + if ($DrawAxisValues) { + if ($LabelsBackground) { + $Options = [ + "DrawBox" => true, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "BoxR" => $LabelsBGR, + "BoxG" => $LabelsBGG, + "BoxB" => $LabelsBGB, + "BoxAlpha" => $LabelsBGAlpha + ]; + } else { + $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; + } + + if ($AxisBoxRounded) { + $Options["BoxRounded"] = true; + } + + $Options["FontName"] = $AxisFontName; + $Options["FontSize"] = $AxisFontSize; + + $Angle = 360 / ($Points * 2); + for ($j = 1; $j <= $Segments; $j++) { + $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; + $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; + $Label = $j * $SegmentHeight; + + $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); + } + } + + /* Axis lines */ + $ID = 0; + for ($i = 0; $i <= 359; $i = $i + $AxisSteps) { + $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; + $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; + + $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); + + if ($WriteLabels) { + $LabelX = cos(deg2rad($i + $AxisRotation)) + * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX + ; + $LabelY = sin(deg2rad($i + $AxisRotation)) + * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY + ; + $Label = $i . "°"; + + if ($LabelPos == RADAR_LABELS_ROTATED) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Angle" => (360 - $i), "Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + } else { + if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMLEFT] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + } + if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPLEFT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMRIGHT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_MIDDLERIGHT] + ); + } + if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPRIGHT] + ); + } + if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { + $Object->drawText( + $LabelX, + $LabelY, + $Label, + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + } + } + } + $ID++; + } + + /* Compute the plots position */ + $ID = 0; + $Plot = []; + foreach ($Data["Series"] as $SerieName => $DataSet) { + if ($SerieName != $LabelSerie) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + foreach ($DataSet["Data"] as $Key => $Value) { + $Angle = $Data["Series"][$LabelSerie]["Data"][$Key]; + $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; + + $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; + $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), + $this->pChartObject->toHTMLColor( + $Palette[$ID]["R"], + $Palette[$ID]["G"], + $Palette[$ID]["B"] + ), + $DataSet["Description"], + $Data["Series"][$LabelSerie]["Data"][$Key] . "° = " . $Value + ); + } + + $Plot[$ID][] = [$X, $Y, $Value]; + } + $ID++; + } + } + + /* Draw all that stuff! */ + foreach ($Plot as $ID => $Points) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + + /* Draw the polygons */ + if ($DrawPoly) { + if ($PolyAlpha != null) { + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $PolyAlpha, + "Surrounding" => $PointSurrounding + ]; + } + $PointsArray = []; + for ($i = 0; $i < count($Points); $i++) { + $PointsArray[] = $Points[$i][0]; + $PointsArray[] = $Points[$i][1]; + } + + $Object->drawPolygon($PointsArray, $Color); + } + + $Color = [ + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"], + "Alpha" => $Palette[$ID]["Alpha"], + "Surrounding" => $PointSurrounding + ]; + + /* Bubble and labels settings */ + $TextSettings = [ + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontName" => $ValueFontName, + "FontSize" => $ValueFontSize, + "R" => $Palette[$ID]["R"], + "G" => $Palette[$ID]["G"], + "B" => $Palette[$ID]["B"] + ]; + $InnerColor = [ + "R" => $InnerBubbleR, + "G" => $InnerBubbleG, + "B" => $InnerBubbleB, + "Alpha" => $InnerBubbleAlpha + ]; + if ($OuterBubbleR != VOID) { + $OuterColor = [ + "R" => $OuterBubbleR, + "G" => $OuterBubbleG, + "B" => $OuterBubbleB, + "Alpha" => $OuterBubbleAlpha + ]; + } else { + $OuterColor = [ + "R" => $Palette[$ID]["R"] + 20, + "G" => $Palette[$ID]["G"] + 20, + "B" => $Palette[$ID]["B"] + 20, + "Alpha" => $Palette[$ID]["Alpha"] + ]; + } + /* Loop to the starting points if asked */ + if ($LineLoopStart && $DrawLines) { + $Object->drawLine( + $Points[count($Points) - 1][0], + $Points[count($Points) - 1][1], + $Points[0][0], + $Points[0][1], + $Color + ); + } + /* Draw the lines & points */ + for ($i = 0; $i < count($Points); $i++) { + if ($DrawLines && $i < count($Points) - 1) { + $Object->drawLine( + $Points[$i][0], + $Points[$i][1], + $Points[$i + 1][0], + $Points[$i + 1][1], + $Color + ); + } + if ($DrawPoints) { + $Object->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $PointRadius, + $Color + ); + } + if ($WriteValuesInBubble && $WriteValues) { + $TxtPos = $this->pChartObject->getTextBox( + $Points[$i][0], + $Points[$i][1], + $ValueFontName, + $ValueFontSize, + 0, + $Points[$i][2] + ); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); + + $this->pChartObject->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $Radius + $OuterBubbleRadius, + $OuterColor + ); + $this->pChartObject->drawFilledCircle( + $Points[$i][0], + $Points[$i][1], + $Radius, + $InnerColor + ); + } + + if ($WriteValues) { + $this->pChartObject->drawText( + $Points[$i][0] - 1, + $Points[$i][1] - 1, + $Points[$i][2], + $TextSettings + ); + } + } + } + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Scatter.php b/vendor/szymach/c-pchart/src/Chart/Scatter.php new file mode 100644 index 0000000..0b1853a --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Scatter.php @@ -0,0 +1,2255 @@ +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /** + * Prepare the scale + * @param array $Format + * @return null|int + * @throws Exception + */ + public function drawScatterScale(array $Format = []) + { + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; + $Floating = isset($Format["Floating"]) ? $Format["Floating"] : false; + $XLabelsRotation = isset($Format["XLabelsRotation"]) ? $Format["XLabelsRotation"] : 90; + $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; + $Factors = isset($Format["Factors"]) ? $Format["Factors"] : [1, 2, 5]; + $ManualScale = isset($Format["ManualScale"]) + ? $Format["ManualScale"] : ["0" => ["Min" => -100, "Max" => 100]] + ; + $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : 0; + $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; + $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; + $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; + $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; + $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : ALL; + $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; + $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; + $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; + $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; + $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; + $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; + $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; + $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; + $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; + $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; + $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; + $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; + $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; + $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : false; + $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; + $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; + $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; + $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; + $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; + $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; + $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; + $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : false; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; + $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : false; + $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; + $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; + $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; + $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 10; + $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; + $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; + $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; + $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 10; + + /* Check if we have at least both one X and Y axis */ + $GotXAxis = false; + $GotYAxis = false; + foreach ($this->pDataObject->Data["Axis"] as $AxisID => $AxisSettings) { + if ($AxisSettings["Identity"] == AXIS_X) { + $GotXAxis = true; + } + if ($AxisSettings["Identity"] == AXIS_Y) { + $GotYAxis = true; + } + } + if (!$GotXAxis) { + return SCATTER_MISSING_X_SERIE; + } + if (!$GotYAxis) { + return SCATTER_MISSING_Y_SERIE; + } + + /* Skip a NOTICE event in case of an empty array */ + if ($DrawYLines == NONE) { + $DrawYLines = ["zarma" => "31"]; + } + + $Data = $this->pDataObject->getData(); + + foreach ($Data["Axis"] as $AxisID => $AxisSettings) { + if ($AxisSettings["Identity"] == AXIS_X) { + $Width = $this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2; + } else { + $Width = $this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $YMargin * 2; + } + + $AxisMin = ABSOLUTE_MAX; + $AxisMax = OUT_OF_SIGHT; + if ($Mode == SCALE_MODE_FLOATING) { + foreach ($Data["Series"] as $SerieID => $SerieParameter) { + if ($SerieParameter["Axis"] == $AxisID + && $Data["Series"][$SerieID]["isDrawable"] + ) { + $AxisMax = max($AxisMax, $Data["Series"][$SerieID]["Max"]); + $AxisMin = min($AxisMin, $Data["Series"][$SerieID]["Min"]); + } + } + $AutoMargin = (($AxisMax - $AxisMin) / 100) * $XReleasePercent; + + $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin; + $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin; + } elseif ($Mode == SCALE_MODE_MANUAL) { + if (isset($ManualScale[$AxisID]["Min"]) + && isset($ManualScale[$AxisID]["Max"]) + ) { + $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; + $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; + } else { + throw new Exception("Manual scale boundaries not set."); + } + } + + /* Full manual scale */ + if (isset($ManualScale[$AxisID]["Rows"]) && isset($ManualScale[$AxisID]["RowHeight"])) { + $Scale = [ + "Rows" => $ManualScale[$AxisID]["Rows"], + "RowHeight" => $ManualScale[$AxisID]["RowHeight"], + "XMin" => $ManualScale[$AxisID]["Min"], + "XMax" => $ManualScale[$AxisID]["Max"] + ]; + } else { + $MaxDivs = floor($Width / $MinDivHeight); + $Scale = $this->pChartObject->computeScale( + $Data["Axis"][$AxisID]["Min"], + $Data["Axis"][$AxisID]["Max"], + $MaxDivs, + $Factors, + $AxisID + ); + } + + $Data["Axis"][$AxisID]["Margin"] = $AxisSettings["Identity"] == AXIS_X ? $XMargin : $YMargin; + $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; + $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; + $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; + $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; + + if (isset($Scale["Format"])) { + $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; + } + + if (!isset($Data["Axis"][$AxisID]["Display"])) { + $Data["Axis"][$AxisID]["Display"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Format"])) { + $Data["Axis"][$AxisID]["Format"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Unit"])) { + $Data["Axis"][$AxisID]["Unit"] = null; + } + } + + /* Get the default font color */ + $FontColorRo = $this->pChartObject->FontColorR; + $FontColorGo = $this->pChartObject->FontColorG; + $FontColorBo = $this->pChartObject->FontColorB; + + /* Set the original boundaries */ + $AxisPos["L"] = $this->pChartObject->GraphAreaX1; + $AxisPos["R"] = $this->pChartObject->GraphAreaX2; + $AxisPos["T"] = $this->pChartObject->GraphAreaY1; + $AxisPos["B"] = $this->pChartObject->GraphAreaY2; + + foreach ($Data["Axis"] as $AxisID => $AxisSettings) { + if (isset($AxisSettings["Color"])) { + $AxisR = $AxisSettings["Color"]["R"]; + $AxisG = $AxisSettings["Color"]["G"]; + $AxisB = $AxisSettings["Color"]["B"]; + $TickR = $AxisSettings["Color"]["R"]; + $TickG = $AxisSettings["Color"]["G"]; + $TickB = $AxisSettings["Color"]["B"]; + $this->pChartObject->setFontProperties( + [ + "R" => $AxisSettings["Color"]["R"], + "G" => $AxisSettings["Color"]["G"], + "B" => $AxisSettings["Color"]["B"] + ] + ); + } else { + $AxisR = $AxisRo; + $AxisG = $AxisGo; + $AxisB = $AxisBo; + $TickR = $TickRo; + $TickG = $TickGo; + $TickB = $TickBo; + $this->pChartObject->setFontProperties( + ["R" => $FontColorRo, "G" => $FontColorGo, "B" => $FontColorBo] + ); + } + + if ($AxisSettings["Identity"] == AXIS_X) { + if ($AxisSettings["Position"] == AXIS_POSITION_BOTTOM) { + if ($XLabelsRotation == 0) { + $LabelAlign = TEXT_ALIGN_TOPMIDDLE; + $LabelOffset = 2; + } + if ($XLabelsRotation > 0 && $XLabelsRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $LabelOffset = 5; + } + if ($XLabelsRotation == 180) { + $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; + $LabelOffset = 5; + } + if ($XLabelsRotation > 180 && $XLabelsRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $LabelOffset = 2; + } + + if ($Floating) { + $FloatingOffset = $YMargin; + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"], + $AxisPos["B"], + $this->pChartObject->GraphAreaX2 - $AxisSettings["Margin"], + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1, + $AxisPos["B"], + $this->pChartObject->GraphAreaX2, + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->pChartObject->drawArrow( + $this->pChartObject->GraphAreaX2 - $AxisSettings["Margin"], + $AxisPos["B"], + $this->pChartObject->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["B"], + ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize] + ); + } + + $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) + - $AxisSettings["Margin"] * 2 + ; + $Step = $Width / $AxisSettings["Rows"]; + $SubTicksSize = $Step / 2; + $MaxBottom = $AxisPos["B"]; + $LastX = null; + for ($i = 0; $i <= $AxisSettings["Rows"]; $i++) { + $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step * $i; + $YPos = $AxisPos["B"]; + $Value = $this->pChartObject->scaleFormat( + $AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"] * $i, + $AxisSettings["Display"], + $AxisSettings["Format"], + $AxisSettings["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastX != null + && $CycleBackground + && ($DrawXLines == ALL || in_array($AxisID, $DrawXLines)) + ) { + $this->pChartObject->drawFilledRectangle( + $LastX, + $this->pChartObject->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->pChartObject->GraphAreaY2 - $FloatingOffset, + $BGColor + ); + } + + if ($DrawXLines == ALL || in_array($AxisID, $DrawXLines)) { + $this->pChartObject->drawLine( + $XPos, + $this->pChartObject->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->pChartObject->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + if ($DrawSubTicks && $i != $AxisSettings["Rows"]) { + $this->pChartObject->drawLine( + $XPos + $SubTicksSize, + $YPos - $InnerSubTickWidth, + $XPos + $SubTicksSize, + $YPos + $OuterSubTickWidth, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->pChartObject->drawLine( + $XPos, + $YPos - $InnerTickWidth, + $XPos, + $YPos + $OuterTickWidth, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos + $OuterTickWidth + $LabelOffset, + $Value, + ["Angle" => $XLabelsRotation, "Align" => $LabelAlign] + ); + $TxtBottom = $YPos + 2 + $OuterTickWidth + 2 + ($Bounds[0]["Y"] - $Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom, $TxtBottom); + + $LastX = $XPos; + } + + if (isset($AxisSettings["Name"])) { + $YPos = $MaxBottom + 2; + $XPos = $this->pChartObject->GraphAreaX1 + + ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / 2 + ; + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos, + $AxisSettings["Name"], + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + $MaxBottom = $Bounds[0]["Y"]; + + $this->pDataObject->Data["GraphArea"]["Y2"] = $MaxBottom + $this->pChartObject->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } elseif ($AxisSettings["Position"] == AXIS_POSITION_TOP) { + if ($XLabelsRotation == 0) { + $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; + $LabelOffset = 2; + } + if ($XLabelsRotation > 0 && $XLabelsRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $LabelOffset = 2; + } + if ($XLabelsRotation == 180) { + $LabelAlign = TEXT_ALIGN_TOPMIDDLE; + $LabelOffset = 5; + } + if ($XLabelsRotation > 180 && $XLabelsRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $LabelOffset = 5; + } + + if ($Floating) { + $FloatingOffset = $YMargin; + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"], + $AxisPos["T"], + $this->pChartObject->GraphAreaX2 - $AxisSettings["Margin"], + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1, + $AxisPos["T"], + $this->pChartObject->GraphAreaX2, + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->pChartObject->drawArrow( + $this->pChartObject->GraphAreaX2 - $AxisSettings["Margin"], + $AxisPos["T"], + $this->pChartObject->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["T"], + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) + - $AxisSettings["Margin"] * 2 + ; + $Step = $Width / $AxisSettings["Rows"]; + $SubTicksSize = $Step / 2; + $MinTop = $AxisPos["T"]; + $LastX = null; + for ($i = 0; $i <= $AxisSettings["Rows"]; $i++) { + $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step * $i; + $YPos = $AxisPos["T"]; + $Value = $this->pChartObject->scaleFormat( + $AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"] * $i, + $AxisSettings["Display"], + $AxisSettings["Format"], + $AxisSettings["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastX != null + && $CycleBackground + && ($DrawXLines == ALL || in_array($AxisID, $DrawXLines)) + ) { + $this->pChartObject->drawFilledRectangle( + $LastX, + $this->pChartObject->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->pChartObject->GraphAreaY2 - $FloatingOffset, + $BGColor + ); + } + + if ($DrawXLines == ALL || in_array($AxisID, $DrawXLines)) { + $this->pChartObject->drawLine( + $XPos, + $this->pChartObject->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->pChartObject->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $AxisSettings["Rows"]) { + $this->pChartObject->drawLine( + $XPos + $SubTicksSize, + $YPos - $OuterSubTickWidth, + $XPos + $SubTicksSize, + $YPos + $InnerSubTickWidth, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->pChartObject->drawLine( + $XPos, + $YPos - $OuterTickWidth, + $XPos, + $YPos + $InnerTickWidth, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos - $OuterTickWidth - $LabelOffset, + $Value, + ["Angle" => $XLabelsRotation, "Align" => $LabelAlign] + ); + $TxtBox = $YPos - $OuterTickWidth - 4 - ($Bounds[0]["Y"] - $Bounds[2]["Y"]); + $MinTop = min($MinTop, $TxtBox); + + $LastX = $XPos; + } + + if (isset($AxisSettings["Name"])) { + $YPos = $MinTop - 2; + $XPos = $this->pChartObject->GraphAreaX1 + + ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / 2 + ; + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos, + $AxisSettings["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $MinTop = $Bounds[2]["Y"]; + + $this->pDataObject->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } + } elseif ($AxisSettings["Identity"] == AXIS_Y) { + if ($AxisSettings["Position"] == AXIS_POSITION_LEFT) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->pChartObject->drawLine( + $AxisPos["L"], + $this->pChartObject->GraphAreaY1 + $AxisSettings["Margin"], + $AxisPos["L"], + $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->pChartObject->drawLine( + $AxisPos["L"], + $this->pChartObject->GraphAreaY1, + $AxisPos["L"], + $this->pChartObject->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->pChartObject->drawArrow( + $AxisPos["L"], + $this->pChartObject->GraphAreaY1 + $AxisSettings["Margin"], + $AxisPos["L"], + $this->pChartObject->GraphAreaY1 - ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) + - $AxisSettings["Margin"] * 2 + ; + $Step = $Height / $AxisSettings["Rows"]; + $SubTicksSize = $Step / 2; + $MinLeft = $AxisPos["L"]; + $LastY = null; + for ($i = 0; $i <= $AxisSettings["Rows"]; $i++) { + $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step * $i; + $XPos = $AxisPos["L"]; + $Value = $this->pChartObject->scaleFormat( + $AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"] * $i, + $AxisSettings["Display"], + $AxisSettings["Format"], + $AxisSettings["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastY != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->pChartObject->drawFilledRectangle( + $this->pChartObject->GraphAreaX1 + $FloatingOffset, + $LastY, + $this->pChartObject->GraphAreaX2 - $FloatingOffset, + $YPos, + $BGColor + ); + } + + if (($YPos != $this->pChartObject->GraphAreaY1 && $YPos != $this->pChartObject->GraphAreaY2) + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->pChartObject->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $AxisSettings["Rows"]) { + $this->pChartObject->drawLine( + $XPos - $OuterSubTickWidth, + $YPos - $SubTicksSize, + $XPos + $InnerSubTickWidth, + $YPos - $SubTicksSize, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->pChartObject->drawLine( + $XPos - $OuterTickWidth, + $YPos, + $XPos + $InnerTickWidth, + $YPos, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->pChartObject->drawText( + $XPos - $OuterTickWidth - 2, + $YPos, + $Value, + ["Align" => TEXT_ALIGN_MIDDLERIGHT] + ); + $TxtLeft = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MinLeft = min($MinLeft, $TxtLeft); + + $LastY = $YPos; + } + + if (isset($AxisSettings["Name"])) { + $XPos = $MinLeft - 2; + $YPos = $this->pChartObject->GraphAreaY1 + + ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / 2 + ; + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos, + $AxisSettings["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90] + ); + $MinLeft = $Bounds[2]["X"]; + + $this->pDataObject->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } elseif ($AxisSettings["Position"] == AXIS_POSITION_RIGHT) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->pChartObject->drawLine( + $AxisPos["R"], + $this->pChartObject->GraphAreaY1 + $AxisSettings["Margin"], + $AxisPos["R"], + $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->pChartObject->drawLine( + $AxisPos["R"], + $this->pChartObject->GraphAreaY1, + $AxisPos["R"], + $this->pChartObject->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->pChartObject->drawArrow( + $AxisPos["R"], + $this->pChartObject->GraphAreaY1 + $AxisSettings["Margin"], + $AxisPos["R"], + $this->pChartObject->GraphAreaY1 - ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) + - $AxisSettings["Margin"] * 2 + ; + $Step = $Height / $AxisSettings["Rows"]; + $SubTicksSize = $Step / 2; + $MaxLeft = $AxisPos["R"]; + $LastY = null; + for ($i = 0; $i <= $AxisSettings["Rows"]; $i++) { + $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step * $i; + $XPos = $AxisPos["R"]; + $Value = $this->pChartObject->scaleFormat( + $AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"] * $i, + $AxisSettings["Display"], + $AxisSettings["Format"], + $AxisSettings["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastY != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->pChartObject->drawFilledRectangle( + $this->pChartObject->GraphAreaX1 + $FloatingOffset, + $LastY, + $this->pChartObject->GraphAreaX2 - $FloatingOffset, + $YPos, + $BGColor + ); + } + + if (($YPos != $this->pChartObject->GraphAreaY1 + && $YPos != $this->pChartObject->GraphAreaY2) + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->pChartObject->drawLine( + $this->pChartObject->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->pChartObject->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $AxisSettings["Rows"]) { + $this->pChartObject->drawLine( + $XPos - $InnerSubTickWidth, + $YPos - $SubTicksSize, + $XPos + $OuterSubTickWidth, + $YPos - $SubTicksSize, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->pChartObject->drawLine( + $XPos - $InnerTickWidth, + $YPos, + $XPos + $OuterTickWidth, + $YPos, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->pChartObject->drawText( + $XPos + $OuterTickWidth + 2, + $YPos, + $Value, + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + $TxtLeft = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MaxLeft = max($MaxLeft, $TxtLeft); + + $LastY = $YPos; + } + + if (isset($AxisSettings["Name"])) { + $XPos = $MaxLeft + 6; + $YPos = $this->pChartObject->GraphAreaY1 + + ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / 2 + ; + $Bounds = $this->pChartObject->drawText( + $XPos, + $YPos, + $AxisSettings["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270] + ); + $MaxLeft = $Bounds[2]["X"]; + + $this->pDataObject->Data["GraphArea"]["X2"] = $MaxLeft + $this->pChartObject->FontSize; + } + + $AxisPos["R"] = $MaxLeft + $ScaleSpacing; + } + } + } + + $this->pDataObject->saveAxisConfig($Data["Axis"]); + } + + /** + * Draw a scatter plot chart + * @param array $Format + */ + public function drawScatterPlotChart($Format = null) + { + $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : 3; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 250; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 250; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 250; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 1; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : null; + + $Data = $this->pDataObject->getData(); + + $BorderColor = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]; + + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + $SerieX = $Series["X"]; + $SerieValuesX = $Data["Series"][$SerieX]["Data"]; + $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; + $SerieValuesY = $Data["Series"][$SerieY]["Data"]; + $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + if ($ImageMapTitle == null) { + $Description = sprintf( + "%s / %s", + $Data["Series"][$Series["X"]]["Description"], + $Data["Series"][$Series["Y"]]["Description"] + ); + } else { + $Description = $ImageMapTitle; + } + + if (isset($Series["Picture"]) && $Series["Picture"] != "") { + $Picture = $Series["Picture"]; + list($PicWidth, $PicHeight, $PicType) = $this->pChartObject->getPicInfo($Picture); + } else { + $Picture = null; + } + + $PosArrayX = $this->getPosArray($SerieValuesX, $SerieXAxis); + if (!is_array($PosArrayX)) { + $Value = $PosArrayX; + $PosArrayX = []; + $PosArrayX[0] = $Value; + } + $PosArrayY = $this->getPosArray($SerieValuesY, $SerieYAxis); + if (!is_array($PosArrayY)) { + $Value = $PosArrayY; + $PosArrayY = []; + $PosArrayY[0] = $Value; + } + + $Color = [ + "R" => $Series["Color"]["R"], + "G" => $Series["Color"]["G"], + "B" => $Series["Color"]["B"], + "Alpha" => $Series["Color"]["Alpha"] + ]; + + foreach ($PosArrayX as $Key => $Value) { + $X = $Value; + $Y = $PosArrayY[$Key]; + + if ($X != VOID && $Y != VOID) { + $RealValue = sprintf( + "%s / %s", + round($Data["Series"][$Series["X"]]["Data"][$Key], 2), + round($Data["Series"][$Series["Y"]]["Data"][$Key], 2) + ); + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + sprintf( + "%s,%s,%s", + floor($X), + floor($Y), + floor($PlotSize + $BorderSize) + ), + $this->pChartObject->toHTMLColor( + $Series["Color"]["R"], + $Series["Color"]["G"], + $Series["Color"]["B"] + ), + $Description, + $RealValue + ); + } + + if (isset($Series["Shape"])) { + $this->pChartObject->drawShape( + $X, + $Y, + $Series["Shape"], + $PlotSize, + $PlotBorder, + $BorderSize, + $Series["Color"]["R"], + $Series["Color"]["G"], + $Series["Color"]["B"], + $Series["Color"]["Alpha"], + $BorderR, + $BorderG, + $BorderB, + $BorderAlpha + ); + } elseif ($Picture == null) { + if ($PlotBorder) { + $this->pChartObject->drawFilledCircle( + $X, + $Y, + $PlotSize + $BorderSize, + $BorderColor + ); + } + $this->pChartObject->drawFilledCircle($X, $Y, $PlotSize, $Color); + } else { + $this->pChartObject->drawFromPicture( + $PicType, + $Picture, + $X - $PicWidth / 2, + $Y - $PicHeight / 2 + ); + } + } + } + } + } + } + + /** + * Draw a scatter line chart + * @param array $Format + */ + public function drawScatterLineChart($Format = null) + { + $Data = $this->pDataObject->getData(); + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : null; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; + + /* Parse all the series to draw */ + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + $SerieX = $Series["X"]; + $SerieValuesX = $Data["Series"][$SerieX]["Data"]; + $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; + $SerieValuesY = $Data["Series"][$SerieY]["Data"]; + $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + $Ticks = $Series["Ticks"]; + $Weight = $Series["Weight"]; + + if ($ImageMapTitle == null) { + $Description = sprintf( + "%s / %s", + $Data["Series"][$Series["X"]]["Description"], + $Data["Series"][$Series["Y"]]["Description"] + ); + } else { + $Description = $ImageMapTitle; + } + + $PosArrayX = $this->getPosArray($SerieValuesX, $SerieXAxis); + if (!is_array($PosArrayX)) { + $Value = $PosArrayX; + $PosArrayX = []; + $PosArrayX[0] = $Value; + } + $PosArrayY = $this->getPosArray($SerieValuesY, $SerieYAxis); + if (!is_array($PosArrayY)) { + $Value = $PosArrayY; + $PosArrayY = []; + $PosArrayY[0] = $Value; + } + + $Color = [ + "R" => $Series["Color"]["R"], + "G" => $Series["Color"]["G"], + "B" => $Series["Color"]["B"], + "Alpha" => $Series["Color"]["Alpha"] + ]; + if ($Ticks != 0) { + $Color["Ticks"] = $Ticks; + } + if ($Weight != 0) { + $Color["Weight"] = $Weight; + } + + $LastX = VOID; + $LastY = VOID; + foreach ($PosArrayX as $Key => $Value) { + $X = $Value; + $Y = $PosArrayY[$Key]; + + if ($X != VOID && $Y != VOID) { + $RealValue = sprintf( + "%s / %s", + round($Data["Series"][$Series["X"]]["Data"][$Key], 2), + round($Data["Series"][$Series["Y"]]["Data"][$Key], 2) + ); + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + sprintf("%s,%s,%s", floor($X), floor($Y), $ImageMapPlotSize), + $this->pChartObject->toHTMLColor( + $Series["Color"]["R"], + $Series["Color"]["G"], + $Series["Color"]["B"] + ), + $Description, + $RealValue + ); + } + } + + if ($X != VOID && $Y != VOID && $LastX != VOID && $LastY != VOID) { + $this->pChartObject->drawLine($LastX, $LastY, $X, $Y, $Color); + } + $LastX = $X; + $LastY = $Y; + } + } + } + } + + /** + * Draw a scatter spline chart + * @param array $Format + */ + public function drawScatterSplineChart(array $Format = []) + { + $Data = $this->pDataObject->getData(); + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : null; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; + + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + $SerieX = $Series["X"]; + $SerieValuesX = $Data["Series"][$SerieX]["Data"]; + $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; + $SerieValuesY = $Data["Series"][$SerieY]["Data"]; + $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + $Ticks = $Series["Ticks"]; + $Weight = $Series["Weight"]; + + if ($ImageMapTitle == null) { + $Description = sprintf( + "%s / %s", + $Data["Series"][$Series["X"]]["Description"], + $Data["Series"][$Series["Y"]]["Description"] + ); + } else { + $Description = $ImageMapTitle; + } + + $PosArrayX = $this->getPosArray($SerieValuesX, $SerieXAxis); + if (!is_array($PosArrayX)) { + $Value = $PosArrayX; + $PosArrayX = []; + $PosArrayX[0] = $Value; + } + $PosArrayY = $this->getPosArray($SerieValuesY, $SerieYAxis); + if (!is_array($PosArrayY)) { + $Value = $PosArrayY; + $PosArrayY = []; + $PosArrayY[0] = $Value; + } + + $SplineSettings = [ + "R" => $Series["Color"]["R"], + "G" => $Series["Color"]["G"], + "B" => $Series["Color"]["B"], + "Alpha" => $Series["Color"]["Alpha"] + ]; + if ($Ticks != 0) { + $SplineSettings["Ticks"] = $Ticks; + } + if ($Weight != 0) { + $SplineSettings["Weight"] = $Weight; + } + + $LastX = VOID; + $LastY = VOID; + $WayPoints = []; + $Forces = []; + foreach ($PosArrayX as $Key => $Value) { + $X = $Value; + $Y = $PosArrayY[$Key]; + $Force = $this->pChartObject->getLength($LastX, $LastY, $X, $Y) / 5; + + if ($X != VOID && $Y != VOID) { + $RealValue = sprintf( + "%s / %s", + round($Data["Series"][$Series["X"]]["Data"][$Key], 2), + round($Data["Series"][$Series["Y"]]["Data"][$Key], 2) + ); + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "CIRCLE", + sprintf("%s,%s,%s", floor($X), floor($Y), $ImageMapPlotSize), + $this->pChartObject->toHTMLColor( + $Series["Color"]["R"], + $Series["Color"]["G"], + $Series["Color"]["B"] + ), + $Description, + $RealValue + ); + } + } + + if ($X != VOID && $Y != VOID) { + $WayPoints[] = [$X, $Y]; + $Forces[] = $Force; + } + + if ($Y == VOID || $X == VOID) { + $SplineSettings["Forces"] = $Forces; + $this->pChartObject->drawSpline($WayPoints, $SplineSettings); + $WayPoints = []; + $Forces = []; + } + + $LastX = $X; + $LastY = $Y; + } + $SplineSettings["Forces"] = $Forces; + $this->pChartObject->drawSpline($WayPoints, $SplineSettings); + } + } + } + + /** + * Return the scaled plot position + * @param mixed $Values + * @param string $AxisID + * @return mixed + */ + public function getPosArray($Values, $AxisID) + { + $Data = $this->pDataObject->getData(); + + if (!is_array($Values)) { + $Values = [$Values]; + } + + if ($Data["Axis"][$AxisID]["Identity"] == AXIS_X) { + $Height = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) + - $Data["Axis"][$AxisID]["Margin"] * 2 + ; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + $Result = []; + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $this->pChartObject->GraphAreaX1 + + $Data["Axis"][$AxisID]["Margin"] + + ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) + ; + } + } + } else { + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) + - $Data["Axis"][$AxisID]["Margin"] * 2 + ; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + $Result = []; + foreach ($Values as $Key => $Value) { + if ($Value == VOID) { + $Result[] = VOID; + } else { + $Result[] = $this->pChartObject->GraphAreaY2 + - $Data["Axis"][$AxisID]["Margin"] + - ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) + ; + } + } + } + return count($Result) == 1 ? reset($Result) : $Result; + } + + /** + * Draw the legend of the active series + * @param int $X + * @param int $Y + * @param array $Format + */ + public function drawScatterLegend($X, $Y, array $Format = []) + { + $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + $Data = $this->pDataObject->getData(); + + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true && isset($Series["Picture"])) { + list($PicWidth, $PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); + if ($IconAreaWidth < $PicWidth) { + $IconAreaWidth = $PicWidth; + } + if ($IconAreaHeight < $PicHeight) { + $IconAreaHeight = $PicHeight; + } + } + } + + $YStep = max($this->pChartObject->FontSize, $IconAreaHeight) + 5; + $XStep = $XSpacing; + + $Boundaries = []; + $Boundaries["L"] = $X; + $Boundaries["T"] = $Y; + $Boundaries["R"] = 0; + $Boundaries["B"] = 0; + $vY = $Y; + $vX = $X; + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + if ($Mode == LEGEND_VERTICAL) { + $BoxArray = $this->pChartObject->getTextBox( + $vX + $IconAreaWidth + 4, + $vY + $IconAreaHeight / 2, + $FontName, + $FontSize, + 0, + $Series["Description"] + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Lines = preg_split("/\n/", $Series["Description"]); + $vY = $vY + max($this->pChartObject->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Series["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->pChartObject->getTextBox( + $vX + $IconAreaWidth + 6, + $Y + $IconAreaHeight / 2 + (($this->pChartObject->FontSize + 3) * $Key), + $FontName, + $FontSize, + 0, + $Value + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Width[] = $BoxArray[1]["X"]; + } + $vX = max($Width) + $XStep; + } + } + } + $vY = $vY - $YStep; + $vX = $vX - $XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) { + $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset; + } + + if ($Style == LEGEND_ROUND) { + $this->pChartObject->drawRoundedFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } elseif ($Style == LEGEND_BOX) { + $this->pChartObject->drawFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + $R = $Series["Color"]["R"]; + $G = $Series["Color"]["G"]; + $B = $Series["Color"]["B"]; + $Ticks = $Series["Ticks"]; + $Weight = $Series["Weight"]; + + if (isset($Series["Picture"])) { + $Picture = $Series["Picture"]; + list($PicWidth, $PicHeight) = $this->pChartObject->getPicInfo($Picture); + $PicX = $X + $IconAreaWidth / 2; + $PicY = $Y + $IconAreaHeight / 2; + + $this->pChartObject->drawFromPNG($PicX - $PicWidth / 2, $PicY - $PicHeight / 2, $Picture); + } else { + if ($Family == LEGEND_FAMILY_BOX) { + if ($BoxWidth != $IconAreaWidth) { + $XOffset = floor(($IconAreaWidth - $BoxWidth) / 2); + } else { + $XOffset = 0; + } + if ($BoxHeight != $IconAreaHeight) { + $YOffset = floor(($IconAreaHeight - $BoxHeight) / 2); + } else { + $YOffset = 0; + } + + $this->pChartObject->drawFilledRectangle( + $X + 1 + $XOffset, + $Y + 1 + $YOffset, + $X + $BoxWidth + $XOffset + 1, + $Y + $BoxHeight + 1 + $YOffset, + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20] + ); + $this->pChartObject->drawFilledRectangle( + $X + $XOffset, + $Y + $YOffset, + $X + $BoxWidth + $XOffset, + $Y + $BoxHeight + $YOffset, + ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20] + ); + } elseif ($Family == LEGEND_FAMILY_CIRCLE) { + $this->pChartObject->drawFilledCircle( + $X + 1 + $IconAreaWidth / 2, + $Y + 1 + $IconAreaHeight / 2, + min($IconAreaHeight / 2, $IconAreaWidth / 2), + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20] + ); + $this->pChartObject->drawFilledCircle( + $X + $IconAreaWidth / 2, + $Y + $IconAreaHeight / 2, + min($IconAreaHeight / 2, $IconAreaWidth / 2), + ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20] + ); + } elseif ($Family == LEGEND_FAMILY_LINE) { + $this->pChartObject->drawLine( + $X + 1, + $Y + 1 + $IconAreaHeight / 2, + $X + 1 + $IconAreaWidth, + $Y + 1 + $IconAreaHeight / 2, + [ + "R" => 0, + "G" => 0, + "B" => 0, + "Alpha" => 20, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + $this->pChartObject->drawLine( + $X, + $Y + $IconAreaHeight / 2, + $X + $IconAreaWidth, + $Y + $IconAreaHeight / 2, + ["R" => $R, "G" => $G, "B" => $B, "Ticks" => $Ticks, "Weight" => $Weight] + ); + } + } + + if ($Mode == LEGEND_VERTICAL) { + $Lines = preg_split("/\n/", $Series["Description"]); + foreach ($Lines as $Key => $Value) { + $this->pChartObject->drawText( + $X + $IconAreaWidth + 4, + $Y + $IconAreaHeight / 2 + (($this->pChartObject->FontSize + 3) * $Key), + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT + ] + ); + } + $Y = $Y + max($this->pChartObject->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Series["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->pChartObject->drawText( + $X + $IconAreaWidth + 4, + $Y + $IconAreaHeight / 2 + (($this->pChartObject->FontSize + 3) * $Key), + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT + ] + ); + $Width[] = $BoxArray[1]["X"]; + } + $X = max($Width) + 2 + $XStep; + } + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + } + + /** + * Get the legend box size + * @param array $Format + * @return array + */ + public function getScatterLegendSize(array $Format = []) + { + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + + $YStep = max($this->pChartObject->FontSize, $BoxSize) + 5; + $XStep = $BoxSize + 5; + + $X = 100; + $Y = 100; + + $Data = $this->pDataObject->getData(); + + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true && isset($Series["Picture"])) { + list($PicWidth, $PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); + if ($IconAreaWidth < $PicWidth) { + $IconAreaWidth = $PicWidth; + } + if ($IconAreaHeight < $PicHeight) { + $IconAreaHeight = $PicHeight; + } + } + } + + $YStep = max($this->pChartObject->FontSize, $IconAreaHeight) + 5; + $XStep = $XSpacing; + + $Boundaries = []; + $Boundaries["L"] = $X; + $Boundaries["T"] = $Y; + $Boundaries["R"] = 0; + $Boundaries["B"] = 0; + $vY = $Y; + $vX = $X; + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + if ($Mode == LEGEND_VERTICAL) { + $BoxArray = $this->pChartObject->getTextBox( + $vX + $IconAreaWidth + 4, + $vY + $IconAreaHeight / 2, + $FontName, + $FontSize, + 0, + $Series["Description"] + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Lines = preg_split("/\n/", $Series["Description"]); + $vY = $vY + max($this->pChartObject->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Series["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->pChartObject->getTextBox( + $vX + $IconAreaWidth + 6, + $Y + $IconAreaHeight / 2 + (($this->pChartObject->FontSize + 3) * $Key), + $FontName, + $FontSize, + 0, + $Value + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Width[] = $BoxArray[1]["X"]; + } + $vX = max($Width) + $XStep; + } + } + } + $vY = $vY - $YStep; + $vX = $vX - $XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ($Boundaries["B"] - ($vY + $BoxSize) < $TopOffset) { + $Boundaries["B"] = $vY + $BoxSize + $TopOffset; + } + + $Width = ($Boundaries["R"] + $Margin) - ($Boundaries["L"] - $Margin); + $Height = ($Boundaries["B"] + $Margin) - ($Boundaries["T"] - $Margin); + + return ["Width" => $Width, "Height" => $Height]; + } + + /** + * Draw the line of best fit + * @param array $Format + */ + public function drawScatterBestFit(array $Format = []) + { + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 0; + + $Data = $this->pDataObject->getData(); + + foreach ($Data["ScatterSeries"] as $Key => $Series) { + if ($Series["isDrawable"] == true) { + $SerieX = $Series["X"]; + $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; + $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + $Color = [ + "R" => $Series["Color"]["R"], + "G" => $Series["Color"]["G"], + "B" => $Series["Color"]["B"], + "Alpha" => $Series["Color"]["Alpha"] + ]; + $Color["Ticks"] = $Ticks; + + $PosArrayX = $Data["Series"][$Series["X"]]["Data"]; + $PosArrayY = $Data["Series"][$Series["Y"]]["Data"]; + + $Sxy = 0; + $Sx = 0; + $Sy = 0; + $Sxx = 0; + foreach ($PosArrayX as $Key => $Value) { + $X = $Value; + $Y = $PosArrayY[$Key]; + + $Sxy = $Sxy + $X * $Y; + $Sx = $Sx + $X; + $Sy = $Sy + $Y; + $Sxx = $Sxx + $X * $X; + } + + $n = count($PosArrayX); + + if ((($n * $Sxx) == ($Sx * $Sx))) { + $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"], $SerieXAxis); + $X2 = $X1; + $Y1 = $this->pChartObject->GraphAreaY1; + $Y2 = $this->pChartObject->GraphAreaY2; + } else { + $M = (($n * $Sxy) - ($Sx * $Sy)) / (($n * $Sxx) - ($Sx * $Sx)); + $B = (($Sy) - ($M * $Sx)) / ($n); + + $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"], $SerieXAxis); + $Y1 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMin"] + $B, $SerieYAxis); + $X2 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMax"], $SerieXAxis); + $Y2 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMax"] + $B, $SerieYAxis); + + $RealM = -($Y2 - $Y1) / ($X2 - $X1); + + if ($Y1 < $this->pChartObject->GraphAreaY1) { + $X1 = $X1 + ($this->pChartObject->GraphAreaY1 - $Y1 / $RealM); + $Y1 = $this->pChartObject->GraphAreaY1; + } + if ($Y1 > $this->pChartObject->GraphAreaY2) { + $X1 = $X1 + ($Y1 - $this->pChartObject->GraphAreaY2) / $RealM; + $Y1 = $this->pChartObject->GraphAreaY2; + } + if ($Y2 < $this->pChartObject->GraphAreaY1) { + $X2 = $X2 - ($this->pChartObject->GraphAreaY1 - $Y2) / $RealM; + $Y2 = $this->pChartObject->GraphAreaY1; + } + if ($Y2 > $this->pChartObject->GraphAreaY2) { + $X2 = $X2 - ($Y2 - $this->pChartObject->GraphAreaY2) / $RealM; + $Y2 = $this->pChartObject->GraphAreaY2; + } + } + + $this->pChartObject->drawLine($X1, $Y1, $X2, $Y2, $Color); + } + } + } + + /** + * + * @param string $ScatterSerieID + * @param mixed $Points + * @param array $Format + * @return null|int + */ + public function writeScatterLabel($ScatterSerieID, $Points, array $Format = []) + { + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : null; + + $Data = $this->pDataObject->getData(); + + if (!is_array($Points)) { + $Points = [$Points]; + } + + if (!isset($Data["ScatterSeries"][$ScatterSerieID])) { + return 0; + } + $Series = $Data["ScatterSeries"][$ScatterSerieID]; + + $SerieX = $Series["X"]; + $SerieValuesX = $Data["Series"][$SerieX]["Data"]; + $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + + $SerieY = $Series["Y"]; + $SerieValuesY = $Data["Series"][$SerieY]["Data"]; + $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + $PosArrayX = $this->getPosArray($SerieValuesX, $SerieXAxis); + if (!is_array($PosArrayX)) { + $Value = $PosArrayX; + $PosArrayX = []; + $PosArrayX[0] = $Value; + } + $PosArrayY = $this->getPosArray($SerieValuesY, $SerieYAxis); + if (!is_array($PosArrayY)) { + $Value = $PosArrayY; + $PosArrayY = []; + $PosArrayY[0] = $Value; + } + + foreach ($Points as $Key => $Point) { + if (isset($PosArrayX[$Point]) && isset($PosArrayY[$Point])) { + $X = floor($PosArrayX[$Point]); + $Y = floor($PosArrayY[$Point]); + + if ($DrawPoint == LABEL_POINT_CIRCLE) { + $this->pChartObject->drawFilledCircle( + $X, + $Y, + 3, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } elseif ($DrawPoint == LABEL_POINT_BOX) { + $this->pChartObject->drawFilledRectangle( + $X - 2, + $Y - 2, + $X + 2, + $Y + 2, + [ + "R" => 255, + "G" => 255, + "B" => 255, + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ] + ); + } + $Serie = []; + $Serie["R"] = $Series["Color"]["R"]; + $Serie["G"] = $Series["Color"]["G"]; + $Serie["B"] = $Series["Color"]["B"]; + $Serie["Alpha"] = $Series["Color"]["Alpha"]; + + $XAxisMode = $Data["Axis"][$SerieXAxis]["Display"]; + $XAxisFormat = $Data["Axis"][$SerieXAxis]["Format"]; + $XAxisUnit = $Data["Axis"][$SerieXAxis]["Unit"]; + if ($Decimals == null) { + $XValue = $SerieValuesX[$Point]; + } else { + $XValue = round($SerieValuesX[$Point], $Decimals); + } + $XValue = $this->pChartObject->scaleFormat($XValue, $XAxisMode, $XAxisFormat, $XAxisUnit); + + $YAxisMode = $Data["Axis"][$SerieYAxis]["Display"]; + $YAxisFormat = $Data["Axis"][$SerieYAxis]["Format"]; + $YAxisUnit = $Data["Axis"][$SerieYAxis]["Unit"]; + if ($Decimals == null) { + $YValue = $SerieValuesY[$Point]; + } else { + $YValue = round($SerieValuesY[$Point], $Decimals); + } + $YValue = $this->pChartObject->scaleFormat($YValue, $YAxisMode, $YAxisFormat, $YAxisUnit); + + $Caption = sprintf("%s / %s", $XValue, $YValue); + + if (isset($Series["Description"])) { + $Description = $Series["Description"]; + } else { + $Description = "No description"; + } + $Series = [["Format" => $Serie, "Caption" => $Caption]]; + + $this->pChartObject->drawLabelBox($X, $Y - 3, $Description, $Series, $Format); + } + } + } + + /** + * Draw a Scatter threshold + * @param mixed $Value + * @param array $Format + * @return array + */ + public function drawScatterThreshold($Value, array $Format = []) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : false; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : false; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + + $CaptionSettings = [ + "DrawBox" => $DrawBox, + "DrawBoxBorder" => $DrawBoxBorder, + "BorderOffset" => $BorderOffset, + "BoxRounded" => $BoxRounded, + "RoundedRadius" => $RoundedRadius, + "BoxR" => $BoxR, + "BoxG" => $BoxG, + "BoxB" => $BoxB, + "BoxAlpha" => $BoxAlpha, + "BoxSurrounding" => $BoxSurrounding, + "BoxBorderR" => $BoxBorderR, + "BoxBorderG" => $BoxBorderG, + "BoxBorderB" => $BoxBorderB, + "BoxBorderAlpha" => $BoxBorderAlpha, + "R" => $CaptionR, + "G" => $CaptionG, + "B" => $CaptionB, + "Alpha" => $CaptionAlpha + ]; + + if ($Caption == null) { + $Caption = $Value; + } + + $Data = $this->pDataObject->getData(); + + if (!isset($Data["Axis"][$AxisID])) { + return -1; + } + + if ($Data["Axis"][$AxisID]["Identity"] == AXIS_Y) { + $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + $Y = $this->getPosArray($Value, $AxisID); + + $this->pChartObject->drawLine( + $X1, + $Y, + $X2, + $Y, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->pChartObject->drawLine( + $X1, + $Y - 1, + $X2, + $Y - 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + $this->pChartObject->drawLine( + $X1, + $Y + 1, + $X2, + $Y + 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $X = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; + } else { + $X = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; + } + $this->pChartObject->drawText($X, $Y, $Caption, $CaptionSettings); + } + + return ["Y" => $Y]; + } elseif ($Data["Axis"][$AxisID]["Identity"] == AXIS_X) { + $X = $this->getPosArray($Value, $AxisID); + $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + + $this->pChartObject->drawLine( + $X, + $Y1, + $X, + $Y2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->pChartObject->drawLine( + $X - 1, + $Y1, + $X - 1, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks] + ); + $this->pChartObject->drawLine( + $X + 1, + $Y1, + $X + 1, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $Y = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + } else { + $Y = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + } + + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + $this->pChartObject->drawText($X, $Y, $Caption, $CaptionSettings); + } + + return ["X" => $X]; + } + } + + /** + * Draw a Scatter threshold area + * @param int|float $Value1 + * @param int|float $Value2 + * @param array $Format + * @return type + */ + public function drawScatterThresholdArea($Value1, $Value2, array $Format = []) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : true; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : "La ouate de phoque"; //null; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : true; + + if ($Value1 > $Value2) { + list($Value1, $Value2) = [$Value2, $Value1]; + } + + $RestoreShadow = $this->pChartObject->Shadow; + if ($DisableShadowOnArea && $this->pChartObject->Shadow) { + $this->pChartObject->Shadow = false; + } + + if ($BorderAlpha > 100) { + $BorderAlpha = 100; + } + + $Data = $this->pDataObject->getData(); + + if (!isset($Data["Axis"][$AxisID])) { + return -1; + } + + if ($Data["Axis"][$AxisID]["Identity"] == AXIS_X) { + $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + $X1 = $this->getPosArray($Value1, $AxisID); + $X2 = $this->getPosArray($Value2, $AxisID); + + if ($X1 <= $this->pChartObject->GraphAreaX1) { + $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + } + if ($X2 >= $this->pChartObject->GraphAreaX2) { + $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + } + + $this->pChartObject->drawFilledRectangle( + $X1, + $Y1, + $X2, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + + if ($Border) { + $this->pChartObject->drawLine( + $X1, + $Y1, + $X1, + $Y2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->pChartObject->drawLine( + $X2, + $Y1, + $X2, + $Y2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($X2 - $X1) / 2 + $X1; + $YPos = ($Y2 - $Y1) / 2 + $Y1; + + if ($NameAngle == ZONE_NAME_ANGLE_AUTO) { + $TxtPos = $this->pChartObject->getTextBox( + $XPos, + $YPos, + $this->pChartObject->FontName, + $this->pChartObject->FontSize, + 0, + $AreaName + ); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + if (abs($X2 - $X1) > $TxtWidth) { + $NameAngle = 0; + } else { + $NameAngle = 90; + } + } + $this->pChartObject->Shadow = $RestoreShadow; + $this->pChartObject->drawText( + $XPos, + $YPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => $NameAngle, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $this->pChartObject->Shadow = false; + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + return ["X1" => $X1, "X2" => $X2]; + } elseif ($Data["Axis"][$AxisID]["Identity"] == AXIS_Y) { + $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + $Y1 = $this->getPosArray($Value1, $AxisID); + $Y2 = $this->getPosArray($Value2, $AxisID); + + if ($Y1 >= $this->pChartObject->GraphAreaY2) { + $Y1 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + } + if ($Y2 <= $this->pChartObject->GraphAreaY1) { + $Y2 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + } + + $this->pChartObject->drawFilledRectangle( + $X1, + $Y1, + $X2, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + + if ($Border) { + $this->pChartObject->drawLine( + $X1, + $Y1, + $X2, + $Y1, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->pChartObject->drawLine( + $X1, + $Y2, + $X2, + $Y2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($X2 - $X1) / 2 + $X1; + $YPos = ($Y2 - $Y1) / 2 + $Y1; + + $this->pChartObject->Shadow = $RestoreShadow; + $this->pChartObject->drawText( + $YPos, + $XPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => 0, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $$this->pChartObject->Shadow = false; + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + return ["Y1" => $Y1, "Y2" => $Y2]; + } + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Split.php b/vendor/szymach/c-pchart/src/Chart/Split.php new file mode 100644 index 0000000..ea8ce4e --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Split.php @@ -0,0 +1,180 @@ +pChartObject = $Object; + + $Spacing = isset($Format["Spacing"]) ? $Format["Spacing"] : 20; + $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 2; + $TextPos = isset($Format["TextPos"]) ? $Format["TextPos"] : TEXT_POS_TOP; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Force = isset($Format["Force"]) ? $Format["Force"] : 70; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 15; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + $LabelSerie = $Data["Abscissa"]; + $DataSerie = []; + + foreach ($Data["Series"] as $SerieName => $Value) { + if ($SerieName != $LabelSerie && empty($DataSerie)) { + $DataSerie = $SerieName; + } + } + + $DataSerieSum = array_sum($Data["Series"][$DataSerie]["Data"]); + $DataSerieCount = count($Data["Series"][$DataSerie]["Data"]); + + /* Scale Processing */ + if ($TextPos == TEXT_POS_RIGHT) { + $YScale = (($Y2 - $Y1) - (($DataSerieCount + 1) * $Spacing)) / $DataSerieSum; + } else { + $YScale = (($Y2 - $Y1) - ($DataSerieCount * $Spacing)) / $DataSerieSum; + } + $LeftHeight = $DataSerieSum * $YScale; + + /* Re-compute graph width depending of the text mode choosen */ + if ($TextPos == TEXT_POS_RIGHT) { + $MaxWidth = 0; + foreach ($Data["Series"][$LabelSerie]["Data"] as $Key => $Label) { + $Boundardies = $Object->getTextBox(0, 0, $Object->FontName, $Object->FontSize, 0, $Label); + if ($Boundardies[1]["X"] > $MaxWidth) { + $MaxWidth = $Boundardies[1]["X"] + $TextPadding * 2; + } + } + $X2 = $X2 - $MaxWidth; + } + + /* Drawing */ + $LeftY = ((($Y2 - $Y1) / 2) + $Y1) - ($LeftHeight / 2); + $RightY = $Y1; + + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) { + if (isset($Data["Series"][$LabelSerie]["Data"][$Key])) { + $Label = $Data["Series"][$LabelSerie]["Data"][$Key]; + } else { + $Label = "-"; + } + $LeftY1 = $LeftY; + $LeftY2 = $LeftY + $Value * $YScale; + + $RightY1 = $RightY + $Spacing; + $RightY2 = $RightY + $Spacing + $Value * $YScale; + + $Settings = [ + "R" => $Palette[$Key]["R"], + "G" => $Palette[$Key]["G"], + "B" => $Palette[$Key]["B"], + "Alpha" => $Palette[$Key]["Alpha"], + "NoDraw" => true, + "Segments" => $Segments, + "Surrounding" => $Surrounding + ]; + + + $Angle = $Object->getAngle($X2, $RightY1, $X1, $LeftY1); + $VectorX1 = cos(deg2rad($Angle + 90)) * $Force + ($X2 - $X1) / 2 + $X1; + $VectorY1 = sin(deg2rad($Angle + 90)) * $Force + ($RightY1 - $LeftY1) / 2 + $LeftY1; + $VectorX2 = cos(deg2rad($Angle - 90)) * $Force + ($X2 - $X1) / 2 + $X1; + $VectorY2 = sin(deg2rad($Angle - 90)) * $Force + ($RightY1 - $LeftY1) / 2 + $LeftY1; + + $Points = $Object->drawBezier( + $X1, + $LeftY1, + $X2, + $RightY1, + $VectorX1, + $VectorY1, + $VectorX2, + $VectorY2, + $Settings + ); + + $PolyGon = []; + foreach ($Points as $Key => $Pos) { + $PolyGon[] = $Pos["X"]; + $PolyGon[] = $Pos["Y"]; + } + + $Angle = $Object->getAngle($X2, $RightY2, $X1, $LeftY2); + $VectorX1 = cos(deg2rad($Angle + 90)) * $Force + ($X2 - $X1) / 2 + $X1; + $VectorY1 = sin(deg2rad($Angle + 90)) * $Force + ($RightY2 - $LeftY2) / 2 + $LeftY2; + $VectorX2 = cos(deg2rad($Angle - 90)) * $Force + ($X2 - $X1) / 2 + $X1; + $VectorY2 = sin(deg2rad($Angle - 90)) * $Force + ($RightY2 - $LeftY2) / 2 + $LeftY2; + + $Points = $Object->drawBezier( + $X1, + $LeftY2, + $X2, + $RightY2, + $VectorX1, + $VectorY1, + $VectorX2, + $VectorY2, + $Settings + ); + $Points = array_reverse($Points); + foreach ($Points as $Key => $Pos) { + $PolyGon[] = $Pos["X"]; + $PolyGon[] = $Pos["Y"]; + } + + $Object->drawPolygon($PolyGon, $Settings); + + if ($TextPos == TEXT_POS_RIGHT) { + $Object->drawText( + $X2 + $TextPadding, + ($RightY2 - $RightY1) / 2 + $RightY1, + $Label, + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + } else { + $Object->drawText( + $X2, + $RightY1 - $TextPadding, + $Label, + ["Align" => TEXT_ALIGN_BOTTOMRIGHT] + ); + } + $LeftY = $LeftY2; + $RightY = $RightY2; + } + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Spring.php b/vendor/szymach/c-pchart/src/Chart/Spring.php new file mode 100644 index 0000000..229000e --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Spring.php @@ -0,0 +1,1190 @@ +Default["R"] = 255; + $this->Default["G"] = 255; + $this->Default["B"] = 255; + $this->Default["Alpha"] = 100; + $this->Default["BorderR"] = 0; + $this->Default["BorderG"] = 0; + $this->Default["BorderB"] = 0; + $this->Default["BorderAlpha"] = 100; + $this->Default["Surrounding"] = null; + $this->Default["BackgroundR"] = 255; + $this->Default["BackgroundG"] = 255; + $this->Default["BackgroundB"] = 255; + $this->Default["BackgroundAlpha"] = 0; + $this->Default["Force"] = 1; + $this->Default["NodeType"] = NODE_TYPE_FREE; + $this->Default["Size"] = 5; + $this->Default["Shape"] = NODE_SHAPE_CIRCLE; + $this->Default["FreeZone"] = 40; + $this->Default["LinkR"] = 0; + $this->Default["LinkG"] = 0; + $this->Default["LinkB"] = 0; + $this->Default["LinkAlpha"] = 0; + + $this->Labels["Type"] = LABEL_CLASSIC; + $this->Labels["R"] = 0; + $this->Labels["G"] = 0; + $this->Labels["B"] = 0; + $this->Labels["Alpha"] = 100; + } + + /** + * Set default links options + * @param array $Settings + */ + public function setLinkDefaults(array $Settings = []) + { + if (isset($Settings["R"])) { + $this->Default["LinkR"] = $Settings["R"]; + } + if (isset($Settings["G"])) { + $this->Default["LinkG"] = $Settings["G"]; + } + if (isset($Settings["B"])) { + $this->Default["LinkB"] = $Settings["B"]; + } + if (isset($Settings["Alpha"])) { + $this->Default["LinkAlpha"] = $Settings["Alpha"]; + } + } + + /** + * Set default links options + * @param array $Settings + */ + public function setLabelsSettings(array $Settings = []) + { + if (isset($Settings["Type"])) { + $this->Labels["Type"] = $Settings["Type"]; + } + if (isset($Settings["R"])) { + $this->Labels["R"] = $Settings["R"]; + } + if (isset($Settings["G"])) { + $this->Labels["G"] = $Settings["G"]; + } + if (isset($Settings["B"])) { + $this->Labels["B"] = $Settings["B"]; + } + if (isset($Settings["Alpha"])) { + $this->Labels["Alpha"] = $Settings["Alpha"]; + } + } + + /** + * Auto compute the FreeZone size based on the number of connections + */ + public function autoFreeZone() + { + /* Check connections reciprocity */ + foreach ($this->Data as $Key => $Settings) { + if (isset($Settings["Connections"])) { + $this->Data[$Key]["FreeZone"] = count($Settings["Connections"]) * 10 + 20; + } else { + $this->Data[$Key]["FreeZone"] = 20; + } + } + } + + /** + * Set link properties + * @param int $FromNode + * @param int $ToNode + * @param array $Settings + * @return null|int + */ + public function linkProperties($FromNode, $ToNode, array $Settings) + { + if (!isset($this->Data[$FromNode])) { + return 0; + } + if (!isset($this->Data[$ToNode])) { + return 0; + } + + $R = isset($Settings["R"]) ? $Settings["R"] : 0; + $G = isset($Settings["G"]) ? $Settings["G"] : 0; + $B = isset($Settings["B"]) ? $Settings["B"] : 0; + $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : 100; + $Name = isset($Settings["Name"]) ? $Settings["Name"] : null; + $Ticks = isset($Settings["Ticks"]) ? $Settings["Ticks"] : null; + + $this->Links[$FromNode][$ToNode]["R"] = $R; + $this->Links[$ToNode][$FromNode]["R"] = $R; + $this->Links[$FromNode][$ToNode]["G"] = $G; + $this->Links[$ToNode][$FromNode]["G"] = $G; + $this->Links[$FromNode][$ToNode]["B"] = $B; + $this->Links[$ToNode][$FromNode]["B"] = $B; + $this->Links[$FromNode][$ToNode]["Alpha"] = $Alpha; + $this->Links[$ToNode][$FromNode]["Alpha"] = $Alpha; + $this->Links[$FromNode][$ToNode]["Name"] = $Name; + $this->Links[$ToNode][$FromNode]["Name"] = $Name; + $this->Links[$FromNode][$ToNode]["Ticks"] = $Ticks; + $this->Links[$ToNode][$FromNode]["Ticks"] = $Ticks; + } + + /** + * @param array $Settings + */ + public function setNodeDefaults(array $Settings = []) + { + if (isset($Settings["R"])) { + $this->Default["R"] = $Settings["R"]; + } + if (isset($Settings["G"])) { + $this->Default["G"] = $Settings["G"]; + } + if (isset($Settings["B"])) { + $this->Default["B"] = $Settings["B"]; + } + if (isset($Settings["Alpha"])) { + $this->Default["Alpha"] = $Settings["Alpha"]; + } + if (isset($Settings["BorderR"])) { + $this->Default["BorderR"] = $Settings["BorderR"]; + } + if (isset($Settings["BorderG"])) { + $this->Default["BorderG"] = $Settings["BorderG"]; + } + if (isset($Settings["BorderB"])) { + $this->Default["BorderB"] = $Settings["BorderB"]; + } + if (isset($Settings["BorderAlpha"])) { + $this->Default["BorderAlpha"] = $Settings["BorderAlpha"]; + } + if (isset($Settings["Surrounding"])) { + $this->Default["Surrounding"] = $Settings["Surrounding"]; + } + if (isset($Settings["BackgroundR"])) { + $this->Default["BackgroundR"] = $Settings["BackgroundR"]; + } + if (isset($Settings["BackgroundG"])) { + $this->Default["BackgroundG"] = $Settings["BackgroundG"]; + } + if (isset($Settings["BackgroundB"])) { + $this->Default["BackgroundB"] = $Settings["BackgroundB"]; + } + if (isset($Settings["BackgroundAlpha"])) { + $this->Default["BackgroundAlpha"] = $Settings["BackgroundAlpha"]; + } + if (isset($Settings["NodeType"])) { + $this->Default["NodeType"] = $Settings["NodeType"]; + } + if (isset($Settings["Size"])) { + $this->Default["Size"] = $Settings["Size"]; + } + if (isset($Settings["Shape"])) { + $this->Default["Shape"] = $Settings["Shape"]; + } + if (isset($Settings["FreeZone"])) { + $this->Default["FreeZone"] = $Settings["FreeZone"]; + } + } + + /** + * Add a node + * @param int $NodeID + * @param array $Settings + * @return null|int + */ + public function addNode($NodeID, array $Settings = []) + { + /* if the node already exists, ignore */ + if (isset($this->Data[$NodeID])) { + return 0; + } + + $Name = isset($Settings["Name"]) ? $Settings["Name"] : "Node " . $NodeID; + $Connections = isset($Settings["Connections"]) ? $Settings["Connections"] : null; + + $R = isset($Settings["R"]) ? $Settings["R"] : $this->Default["R"]; + $G = isset($Settings["G"]) ? $Settings["G"] : $this->Default["G"]; + $B = isset($Settings["B"]) ? $Settings["B"] : $this->Default["B"]; + $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : $this->Default["Alpha"]; + $BorderR = isset($Settings["BorderR"]) ? $Settings["BorderR"] : $this->Default["BorderR"]; + $BorderG = isset($Settings["BorderG"]) ? $Settings["BorderG"] : $this->Default["BorderG"]; + $BorderB = isset($Settings["BorderB"]) ? $Settings["BorderB"] : $this->Default["BorderB"]; + $BorderAlpha = isset($Settings["BorderAlpha"]) ? $Settings["BorderAlpha"] : $this->Default["BorderAlpha"]; + $Surrounding = isset($Settings["Surrounding"]) ? $Settings["Surrounding"] : $this->Default["Surrounding"]; + $BackgroundR = isset($Settings["BackgroundR"]) ? $Settings["BackgroundR"] : $this->Default["BackgroundR"]; + $BackgroundG = isset($Settings["BackgroundG"]) ? $Settings["BackgroundG"] : $this->Default["BackgroundG"]; + $BackgroundB = isset($Settings["BackgroundB"]) ? $Settings["BackgroundB"] : $this->Default["BackgroundB"]; + $BackgroundAlpha = isset($Settings["BackgroundAlpha"]) + ? $Settings["BackgroundAlpha"] : $this->Default["BackgroundAlpha"] + ; + $Force = isset($Settings["Force"]) ? $Settings["Force"] : $this->Default["Force"]; + $NodeType = isset($Settings["NodeType"]) ? $Settings["NodeType"] : $this->Default["NodeType"]; + $Size = isset($Settings["Size"]) ? $Settings["Size"] : $this->Default["Size"]; + $Shape = isset($Settings["Shape"]) ? $Settings["Shape"] : $this->Default["Shape"]; + $FreeZone = isset($Settings["FreeZone"]) ? $Settings["FreeZone"] : $this->Default["FreeZone"]; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + $this->Data[$NodeID]["R"] = $R; + $this->Data[$NodeID]["G"] = $G; + $this->Data[$NodeID]["B"] = $B; + $this->Data[$NodeID]["Alpha"] = $Alpha; + $this->Data[$NodeID]["BorderR"] = $BorderR; + $this->Data[$NodeID]["BorderG"] = $BorderG; + $this->Data[$NodeID]["BorderB"] = $BorderB; + $this->Data[$NodeID]["BorderAlpha"] = $BorderAlpha; + $this->Data[$NodeID]["BackgroundR"] = $BackgroundR; + $this->Data[$NodeID]["BackgroundG"] = $BackgroundG; + $this->Data[$NodeID]["BackgroundB"] = $BackgroundB; + $this->Data[$NodeID]["BackgroundAlpha"] = $BackgroundAlpha; + $this->Data[$NodeID]["Name"] = $Name; + $this->Data[$NodeID]["Force"] = $Force; + $this->Data[$NodeID]["Type"] = $NodeType; + $this->Data[$NodeID]["Size"] = $Size; + $this->Data[$NodeID]["Shape"] = $Shape; + $this->Data[$NodeID]["FreeZone"] = $FreeZone; + if ($Connections != null) { + if (is_array($Connections)) { + foreach ($Connections as $Key => $Value) { + $this->Data[$NodeID]["Connections"][] = $Value; + } + } else { + $this->Data[$NodeID]["Connections"][] = $Connections; + } + } + } + + /** + * Set color attribute for a list of nodes + * @param array|string $Nodes + * @param array $Settings + */ + public function setNodesColor($Nodes, array $Settings = []) + { + if (is_array($Nodes)) { + foreach ($Nodes as $Key => $NodeID) { + if (isset($this->Data[$NodeID])) { + if (isset($Settings["R"])) { + $this->Data[$NodeID]["R"] = $Settings["R"]; + } + if (isset($Settings["G"])) { + $this->Data[$NodeID]["G"] = $Settings["G"]; + } + if (isset($Settings["B"])) { + $this->Data[$NodeID]["B"] = $Settings["B"]; + } + if (isset($Settings["Alpha"])) { + $this->Data[$NodeID]["Alpha"] = $Settings["Alpha"]; + } + if (isset($Settings["BorderR"])) { + $this->Data[$NodeID]["BorderR"] = $Settings["BorderR"]; + } + if (isset($Settings["BorderG"])) { + $this->Data[$NodeID]["BorderG"] = $Settings["BorderG"]; + } + if (isset($Settings["BorderB"])) { + $this->Data[$NodeID]["BorderB"] = $Settings["BorderB"]; + } + if (isset($Settings["BorderAlpha"])) { + $this->Data[$NodeID]["BorderAlpha"] = $Settings["BorderAlpha"]; + } + if (isset($Settings["Surrounding"])) { + $this->Data[$NodeID]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; + $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; + $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; + } + } + } + } else { + if (isset($Settings["R"])) { + $this->Data[$Nodes]["R"] = $Settings["R"]; + } + if (isset($Settings["G"])) { + $this->Data[$Nodes]["G"] = $Settings["G"]; + } + if (isset($Settings["B"])) { + $this->Data[$Nodes]["B"] = $Settings["B"]; + } + if (isset($Settings["Alpha"])) { + $this->Data[$Nodes]["Alpha"] = $Settings["Alpha"]; + } + if (isset($Settings["BorderR"])) { + $this->Data[$Nodes]["BorderR"] = $Settings["BorderR"]; + } + if (isset($Settings["BorderG"])) { + $this->Data[$Nodes]["BorderG"] = $Settings["BorderG"]; + } + if (isset($Settings["BorderB"])) { + $this->Data[$Nodes]["BorderB"] = $Settings["BorderB"]; + } + if (isset($Settings["BorderAlpha"])) { + $this->Data[$Nodes]["BorderAlpha"] = $Settings["BorderAlpha"]; + } + if (isset($Settings["Surrounding"])) { + $this->Data[$Nodes]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; + $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; + $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; + } + } + } + + /** + * Returns all the nodes details + * @return array + */ + public function dumpNodes() + { + return $this->Data; + } + + /** + * Check if a connection exists and create it if required + * @param string|int $SourceID + * @param string|int $TargetID + * @return boolean|null + */ + public function checkConnection($SourceID, $TargetID) + { + if (isset($this->Data[$SourceID]["Connections"])) { + foreach ($this->Data[$SourceID]["Connections"] as $ConnectionID) { + if ($TargetID == $ConnectionID) { + return true; + } + } + } + $this->Data[$SourceID]["Connections"][] = $TargetID; + } + + /** + * Get the median linked nodes position + * @param string $Key + * @param int $X + * @param int $Y + * @return array + */ + public function getMedianOffset($Key, $X, $Y) + { + $Cpt = 1; + if (isset($this->Data[$Key]["Connections"])) { + foreach ($this->Data[$Key]["Connections"] as $NodeID) { + if (isset($this->Data[$NodeID]["X"]) + && isset($this->Data[$NodeID]["Y"]) + ) { + $X = $X + $this->Data[$NodeID]["X"]; + $Y = $Y + $this->Data[$NodeID]["Y"]; + $Cpt++; + } + } + } + return ["X" => $X / $Cpt, "Y" => $Y / $Cpt]; + } + + /** + * Return the ID of the attached partner with the biggest weight + * @param string $Key + * @return string + */ + public function getBiggestPartner($Key) + { + if (!isset($this->Data[$Key]["Connections"])) { + return ""; + } + + $MaxWeight = 0; + $Result = ""; + foreach ($this->Data[$Key]["Connections"] as $Key => $PeerID) { + if ($this->Data[$PeerID]["Weight"] > $MaxWeight) { + $MaxWeight = $this->Data[$PeerID]["Weight"]; + $Result = $PeerID; + } + } + return $Result; + } + + /** + * Do the initial node positions computing pass + * @param int $Algorithm + */ + public function firstPass($Algorithm) + { + $CenterX = ($this->X2 - $this->X1) / 2 + $this->X1; + $CenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; + + /* Check connections reciprocity */ + foreach ($this->Data as $Key => $Settings) { + if (isset($Settings["Connections"])) { + foreach ($Settings["Connections"] as $ID => $ConnectionID) { + $this->checkConnection($ConnectionID, $Key); + } + } + } + + if ($this->AutoComputeFreeZone) { + $this->autoFreeZone(); + } + + /* Get the max number of connections */ + $MaxConnections = 0; + foreach ($this->Data as $Key => $Settings) { + if (isset($Settings["Connections"])) { + if ($MaxConnections < count($Settings["Connections"])) { + $MaxConnections = count($Settings["Connections"]); + } + } + } + + if ($Algorithm == ALGORITHM_WEIGHTED) { + foreach ($this->Data as $Key => $Settings) { + if ($Settings["Type"] == NODE_TYPE_CENTRAL) { + $this->Data[$Key]["X"] = $CenterX; + $this->Data[$Key]["Y"] = $CenterY; + } + if ($Settings["Type"] == NODE_TYPE_FREE) { + if (isset($Settings["Connections"])) { + $Connections = count($Settings["Connections"]); + } else { + $Connections = 0; + } + + $Ring = $MaxConnections - $Connections; + $Angle = rand(0, 360); + + $this->Data[$Key]["X"] = cos(deg2rad($Angle)) * ($Ring * $this->RingSize) + $CenterX; + $this->Data[$Key]["Y"] = sin(deg2rad($Angle)) * ($Ring * $this->RingSize) + $CenterY; + } + } + } elseif ($Algorithm == ALGORITHM_CENTRAL) { + /* Put a weight on each nodes */ + foreach ($this->Data as $Key => $Settings) { + if (isset($Settings["Connections"])) { + $this->Data[$Key]["Weight"] = count($Settings["Connections"]); + } else { + $this->Data[$Key]["Weight"] = 0; + } + } + + $MaxConnections = $MaxConnections + 1; + for ($i = $MaxConnections; $i >= 0; $i--) { + foreach ($this->Data as $Key => $Settings) { + if ($Settings["Type"] == NODE_TYPE_CENTRAL) { + $this->Data[$Key]["X"] = $CenterX; + $this->Data[$Key]["Y"] = $CenterY; + } + if ($Settings["Type"] == NODE_TYPE_FREE) { + if (isset($Settings["Connections"])) { + $Connections = count($Settings["Connections"]); + } else { + $Connections = 0; + } + + if ($Connections == $i) { + $BiggestPartner = $this->getBiggestPartner($Key); + if ($BiggestPartner != "") { + $Ring = $this->Data[$BiggestPartner]["FreeZone"]; + $Weight = $this->Data[$BiggestPartner]["Weight"]; + $AngleDivision = 360 / $this->Data[$BiggestPartner]["Weight"]; + $Done = false; + $Tries = 0; + while (!$Done && $Tries <= $Weight * 2) { + $Tries++; + $Angle = floor(rand(0, $Weight) * $AngleDivision); + if (!isset($this->Data[$BiggestPartner]["Angular"][$Angle]) + || !isset($this->Data[$BiggestPartner]["Angular"]) + ) { + $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; + $Done = true; + } + } + if (!$Done) { + $Angle = rand(0, 360); + $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; + } + + $X = cos(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["X"]; + $Y = sin(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["Y"]; + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + } + } + } + } + } elseif ($Algorithm == ALGORITHM_CIRCULAR) { + $MaxConnections = $MaxConnections + 1; + for ($i = $MaxConnections; $i >= 0; $i--) { + foreach ($this->Data as $Key => $Settings) { + if ($Settings["Type"] == NODE_TYPE_CENTRAL) { + $this->Data[$Key]["X"] = $CenterX; + $this->Data[$Key]["Y"] = $CenterY; + } + if ($Settings["Type"] == NODE_TYPE_FREE) { + if (isset($Settings["Connections"])) { + $Connections = count($Settings["Connections"]); + } else { + $Connections = 0; + } + + if ($Connections == $i) { + $Ring = $MaxConnections - $Connections; + $Angle = rand(0, 360); + + $X = cos(deg2rad($Angle)) * ($Ring * $this->RingSize) + $CenterX; + $Y = sin(deg2rad($Angle)) * ($Ring * $this->RingSize) + $CenterY; + + $MedianOffset = $this->getMedianOffset($Key, $X, $Y); + + $this->Data[$Key]["X"] = $MedianOffset["X"]; + $this->Data[$Key]["Y"] = $MedianOffset["Y"]; + } + } + } + } + } elseif ($Algorithm == ALGORITHM_RANDOM) { + foreach ($this->Data as $Key => $Settings) { + if ($Settings["Type"] == NODE_TYPE_FREE) { + $this->Data[$Key]["X"] = $CenterX + rand(-20, 20); + $this->Data[$Key]["Y"] = $CenterY + rand(-20, 20); + } + if ($Settings["Type"] == NODE_TYPE_CENTRAL) { + $this->Data[$Key]["X"] = $CenterX; + $this->Data[$Key]["Y"] = $CenterY; + } + } + } + } + + /** + * Compute one pass + */ + public function doPass() + { + /* Compute vectors */ + foreach ($this->Data as $Key => $Settings) { + if ($Settings["Type"] != NODE_TYPE_CENTRAL) { + unset($this->Data[$Key]["Vectors"]); + + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + /* Repulsion vectors */ + foreach ($this->Data as $Key2 => $Settings2) { + if ($Key != $Key2) { + $X2 = $this->Data[$Key2]["X"]; + $Y2 = $this->Data[$Key2]["Y"]; + $FreeZone = $this->Data[$Key2]["FreeZone"]; + + $Distance = $this->getDistance($X1, $Y1, $X2, $Y2); + $Angle = $this->getAngle($X1, $Y1, $X2, $Y2) + 180; + + /* Nodes too close, repulsion occurs */ + if ($Distance < $FreeZone) { + $Force = log(pow(2, $FreeZone - $Distance)); + if ($Force > 1) { + $this->Data[$Key]["Vectors"][] = [ + "Type" => "R", + "Angle" => ((int) $Angle) % 360, + "Force" => $Force + ]; + } + } + } + } + + /* Attraction vectors */ + if (isset($Settings["Connections"])) { + foreach ($Settings["Connections"] as $ID => $NodeID) { + if (isset($this->Data[$NodeID])) { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + $FreeZone = $this->Data[$Key2]["FreeZone"]; + + $Distance = $this->getDistance($X1, $Y1, $X2, $Y2); + $Angle = $this->getAngle($X1, $Y1, $X2, $Y2); + + if ($Distance > $FreeZone) { + $Force = log(($Distance - $FreeZone) + 1); + } else { + $Force = log(($FreeZone - $Distance) + 1); + ($Angle = $Angle + 180); + } + + if ($Force > 1) { + $this->Data[$Key]["Vectors"][] = [ + "Type" => "A", + "Angle" => ((int) $Angle) % 360, + "Force" => $Force + ]; + } + } + } + } + } + } + + /* Move the nodes accoding to the vectors */ + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if (isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL) { + foreach ($Settings["Vectors"] as $ID => $Vector) { + $Type = $Vector["Type"]; + $Force = $Vector["Force"]; + $Angle = $Vector["Angle"]; + $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; + + $X = cos(deg2rad($Angle)) * $Force * $Factor + $X; + $Y = sin(deg2rad($Angle)) * $Force * $Factor + $Y; + } + } + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + } + + /** + * @return int|float + */ + public function lastPass() + { + /* Put everything inside the graph area */ + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ($X < $this->X1) { + $X = $this->X1; + } + if ($X > $this->X2) { + $X = $this->X2; + } + if ($Y < $this->Y1) { + $Y = $this->Y1; + } + if ($Y > $this->Y2) { + $Y = $this->Y2; + } + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + + /* Dump all links */ + $Links = []; + foreach ($this->Data as $Key => $Settings) { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if (isset($Settings["Connections"])) { + foreach ($Settings["Connections"] as $ID => $NodeID) { + if (isset($this->Data[$NodeID])) { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + + $Links[] = [ + "X1" => $X1, + "Y1" => $Y1, + "X2" => $X2, + "Y2" => $Y2, + "Source" => $Settings["Name"], + "Destination" => $this->Data[$NodeID]["Name"] + ]; + } + } + } + } + + /* Check collisions */ + $Conflicts = 0; + foreach ($this->Data as $Key => $Settings) { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if (isset($Settings["Connections"])) { + foreach ($Settings["Connections"] as $ID => $NodeID) { + if (isset($this->Data[$NodeID])) { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + + foreach ($Links as $IDLinks => $Link) { + $X3 = $Link["X1"]; + $Y3 = $Link["Y1"]; + $X4 = $Link["X2"]; + $Y4 = $Link["Y2"]; + + if (!($X1 == $X3 && $X2 == $X4 && $Y1 == $Y3 && $Y2 == $Y4)) { + if ($this->intersect($X1, $Y1, $X2, $Y2, $X3, $Y3, $X4, $Y4)) { + if ($Link["Source"] != $Settings["Name"] + && $Link["Source"] != $this->Data[$NodeID]["Name"] + && $Link["Destination"] != $Settings["Name"] + && $Link["Destination"] != $this->Data[$NodeID]["Name"] + ) { + $Conflicts++; + } + } + } + } + } + } + } + } + return $Conflicts / 2; + } + + /** + * Center the graph + */ + public function center() + { + /* Determine the real center */ + $TargetCenterX = ($this->X2 - $this->X1) / 2 + $this->X1; + $TargetCenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; + + /* Get current boundaries */ + $XMin = $this->X2; + $XMax = $this->X1; + $YMin = $this->Y2; + $YMax = $this->Y1; + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ($X < $XMin) { + $XMin = $X; + } + if ($X > $XMax) { + $XMax = $X; + } + if ($Y < $YMin) { + $YMin = $Y; + } + if ($Y > $YMax) { + $YMax = $Y; + } + } + $CurrentCenterX = ($XMax - $XMin) / 2 + $XMin; + $CurrentCenterY = ($YMax - $YMin) / 2 + $YMin; + + /* Compute the offset to apply */ + $XOffset = $TargetCenterX - $CurrentCenterX; + $YOffset = $TargetCenterY - $CurrentCenterY; + + /* Correct the points position */ + foreach ($this->Data as $Key => $Settings) { + $this->Data[$Key]["X"] = $Settings["X"] + $XOffset; + $this->Data[$Key]["Y"] = $Settings["Y"] + $YOffset; + } + } + + /** + * Create the encoded string + * @param Image $Object + * @param string $Settings + * @return array + */ + public function drawSpring(Image $Object, array $Settings = []) + { + $this->pChartObject = $Object; + + $Pass = isset($Settings["Pass"]) ? $Settings["Pass"] : 50; + $Retries = isset($Settings["Retry"]) ? $Settings["Retry"] : 10; + $this->MagneticForceA = isset($Settings["MagneticForceA"]) ? $Settings["MagneticForceA"] : 1.5; + $this->MagneticForceR = isset($Settings["MagneticForceR"]) ? $Settings["MagneticForceR"] : 2; + $this->RingSize = isset($Settings["RingSize"]) ? $Settings["RingSize"] : 40; + $DrawVectors = isset($Settings["DrawVectors"]) ? $Settings["DrawVectors"] : false; + $DrawQuietZone = isset($Settings["DrawQuietZone"]) ? $Settings["DrawQuietZone"] : false; + $CenterGraph = isset($Settings["CenterGraph"]) ? $Settings["CenterGraph"] : true; + $TextPadding = isset($Settings["TextPadding"]) ? $Settings["TextPadding"] : 4; + $Algorithm = isset($Settings["Algorithm"]) ? $Settings["Algorithm"] : ALGORITHM_WEIGHTED; + + $this->X1 = $Object->GraphAreaX1; + $this->Y1 = $Object->GraphAreaY1; + $this->X2 = $Object->GraphAreaX2; + $this->Y2 = $Object->GraphAreaY2; + + $Conflicts = 1; + $Jobs = 0; + $this->History["MinimumConflicts"] = -1; + while ($Conflicts != 0 && $Jobs < $Retries) { + $Jobs++; + + /* Compute the initial settings */ + $this->firstPass($Algorithm); + + /* Apply the vectors */ + if ($Pass > 0) { + for ($i = 0; $i <= $Pass; $i++) { + $this->doPass(); + } + } + + $Conflicts = $this->lastPass(); + if ($this->History["MinimumConflicts"] == -1 + || $Conflicts < $this->History["MinimumConflicts"] + ) { + $this->History["MinimumConflicts"] = $Conflicts; + $this->History["Result"] = $this->Data; + } + } + + $Conflicts = $this->History["MinimumConflicts"]; + $this->Data = $this->History["Result"]; + + if ($CenterGraph) { + $this->center(); + } + + /* Draw the connections */ + $Drawn = []; + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if (isset($Settings["Connections"])) { + foreach ($Settings["Connections"] as $ID => $NodeID) { + if (!isset($Drawn[$Key])) { + $Drawn[$Key] = ""; + } + if (!isset($Drawn[$NodeID])) { + $Drawn[$NodeID] = ""; + } + + if (isset($this->Data[$NodeID]) + && !isset($Drawn[$Key][$NodeID]) + && !isset($Drawn[$NodeID][$Key]) + ) { + $Color = [ + "R" => $this->Default["LinkR"], + "G" => $this->Default["LinkG"], + "B" => $this->Default["LinkB"], + "Alpha" => $this->Default["Alpha"] + ]; + + if (count($this->Links)) { + if (isset($this->Links[$Key][$NodeID]["R"])) { + $Color = [ + "R" => $this->Links[$Key][$NodeID]["R"], + "G" => $this->Links[$Key][$NodeID]["G"], + "B" => $this->Links[$Key][$NodeID]["B"], + "Alpha" => $this->Links[$Key][$NodeID]["Alpha"] + ]; + } + + if (isset($this->Links[$Key][$NodeID]["Ticks"])) { + $Color["Ticks"] = $this->Links[$Key][$NodeID]["Ticks"]; + } + } + + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + $this->pChartObject->drawLine($X, $Y, $X2, $Y2, $Color); + $Drawn[$Key][$NodeID] = true; + + if (isset($this->Links) && count($this->Links)) { + if (isset($this->Links[$Key][$NodeID]["Name"]) + || isset($this->Links[$NodeID][$Key]["Name"]) + ) { + $Name = isset($this->Links[$Key][$NodeID]["Name"]) + ? $this->Links[$Key][$NodeID]["Name"] + : $this->Links[$NodeID][$Key]["Name"] + ; + $TxtX = ($X2 - $X) / 2 + $X; + $TxtY = ($Y2 - $Y) / 2 + $Y; + + if ($X <= $X2) { + $Angle = (360 - $this->getAngle($X, $Y, $X2, $Y2)) % 360; + } else { + $Angle = (360 - $this->getAngle($X2, $Y2, $X, $Y)) % 360; + } + $Settings = $Color; + $Settings["Angle"] = $Angle; + $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + $this->pChartObject->drawText($TxtX, $TxtY, $Name, $Settings); + } + } + } + } + } + } + + /* Draw the quiet zones */ + if ($DrawQuietZone) { + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + $FreeZone = $Settings["FreeZone"]; + + $this->pChartObject->drawFilledCircle( + $X, + $Y, + $FreeZone, + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 2] + ); + } + } + + + /* Draw the nodes */ + foreach ($this->Data as $Key => $Settings) { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + $Name = $Settings["Name"]; + $FreeZone = $Settings["FreeZone"]; + $Shape = $Settings["Shape"]; + $Size = $Settings["Size"]; + + $Color = [ + "R" => $Settings["R"], + "G" => $Settings["G"], + "B" => $Settings["B"], + "Alpha" => $Settings["Alpha"], + "BorderR" => $Settings["BorderR"], + "BorderG" => $Settings["BorderG"], + "BorderB" => $Settings["BorderB"], + "BorderApha" => $Settings["BorderAlpha"] + ]; + + if ($Shape == NODE_SHAPE_CIRCLE) { + $this->pChartObject->drawFilledCircle($X, $Y, $Size, $Color); + } elseif ($Shape == NODE_SHAPE_TRIANGLE) { + $Points = []; + $Points[] = cos(deg2rad(270)) * $Size + $X; + $Points[] = sin(deg2rad(270)) * $Size + $Y; + $Points[] = cos(deg2rad(45)) * $Size + $X; + $Points[] = sin(deg2rad(45)) * $Size + $Y; + $Points[] = cos(deg2rad(135)) * $Size + $X; + $Points[] = sin(deg2rad(135)) * $Size + $Y; + $this->pChartObject->drawPolygon($Points, $Color); + } elseif ($Shape == NODE_SHAPE_SQUARE) { + $Offset = $Size / 2; + $Size = $Size / 2; + $this->pChartObject->drawFilledRectangle( + $X - $Offset, + $Y - $Offset, + $X + $Offset, + $Y + $Offset, + $Color + ); + } + + if ($Name != "") { + $LabelOptions = [ + "R" => $this->Labels["R"], + "G" => $this->Labels["G"], + "B" => $this->Labels["B"], + "Alpha" => $this->Labels["Alpha"] + ]; + + if ($this->Labels["Type"] == LABEL_LIGHT) { + $LabelOptions["Align"] = TEXT_ALIGN_BOTTOMLEFT; + $this->pChartObject->drawText($X, $Y, $Name, $LabelOptions); + } elseif ($this->Labels["Type"] == LABEL_CLASSIC) { + $LabelOptions["Align"] = TEXT_ALIGN_TOPMIDDLE; + $LabelOptions["DrawBox"] = true; + $LabelOptions["BoxAlpha"] = 50; + $LabelOptions["BorderOffset"] = 4; + $LabelOptions["RoundedRadius"] = 3; + $LabelOptions["BoxRounded"] = true; + $LabelOptions["NoShadow"] = true; + + $this->pChartObject->drawText($X, $Y + $Size + $TextPadding, $Name, $LabelOptions); + } + } + } + + /* Draw the vectors */ + if ($DrawVectors) { + foreach ($this->Data as $Key => $Settings) { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if (isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL) { + foreach ($Settings["Vectors"] as $ID => $Vector) { + $Type = $Vector["Type"]; + $Force = $Vector["Force"]; + $Angle = $Vector["Angle"]; + $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; + $Color = $Type == "A" + ? ["FillR" => 255, "FillG" => 0, "FillB" => 0] + : ["FillR" => 0, "FillG" => 255, "FillB" => 0] + ; + + $X2 = cos(deg2rad($Angle)) * $Force * $Factor + $X1; + $Y2 = sin(deg2rad($Angle)) * $Force * $Factor + $Y1; + + $this->pChartObject->drawArrow($X1, $Y1, $X2, $Y2, $Color); + } + } + } + } + + return ["Pass" => $Jobs, "Conflicts" => $Conflicts]; + } + + /** + * Return the distance between two points + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @return int|float + */ + public function getDistance($X1, $Y1, $X2, $Y2) + { + return sqrt(($X2 - $X1) * ($X2 - $X1) + ($Y2 - $Y1) * ($Y2 - $Y1)); + } + + /** + * Return the angle made by a line and the X axis + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @return int|float + */ + public function getAngle($X1, $Y1, $X2, $Y2) + { + $Opposite = $Y2 - $Y1; + $Adjacent = $X2 - $X1; + $Angle = rad2deg(atan2($Opposite, $Adjacent)); + + return $Angle > 0 ? $Angle : 360 - abs($Angle); + } + + /** + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param int $X3 + * @param int $Y3 + * @param int $X4 + * @param int $Y4 + * @return boolean + */ + public function intersect($X1, $Y1, $X2, $Y2, $X3, $Y3, $X4, $Y4) + { + $A = (($X3 * $Y4 - $X4 * $Y3) * ($X1 - $X2) - ($X1 * $Y2 - $X2 * $Y1) * ($X3 - $X4)); + $B = (($Y1 - $Y2) * ($X3 - $X4) - ($Y3 - $Y4) * ($X1 - $X2)); + + if ($B == 0) { + return false; + } + $Xi = $A / $B; + + $C = ($X1 - $X2); + if ($C == 0) { + return false; + } + $Yi = $Xi * (($Y1 - $Y2) / $C) + (($X1 * $Y2 - $X2 * $Y1) / $C); + + if ($Xi >= min($X1, $X2) + && $Xi >= min($X3, $X4) + && $Xi <= max($X1, $X2) + && $Xi <= max($X3, $X4) + && $Yi >= min($Y1, $Y2) + && $Yi >= min($Y3, $Y4) + && $Yi <= max($Y1, $Y2) + && $Yi <= max($Y3, $Y4) + ) { + return true; + } + + return false; + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Stock.php b/vendor/szymach/c-pchart/src/Chart/Stock.php new file mode 100644 index 0000000..925ceea --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Stock.php @@ -0,0 +1,453 @@ +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /** + * Draw a stock chart + * @param array $Format + * @return integer|null + */ + public function drawStockChart(array $Format = []) + { + $SerieOpen = isset($Format["SerieOpen"]) ? $Format["SerieOpen"] : "Open"; + $SerieClose = isset($Format["SerieClose"]) ? $Format["SerieClose"] : "Close"; + $SerieMin = isset($Format["SerieMin"]) ? $Format["SerieMin"] : "Min"; + $SerieMax = isset($Format["SerieMax"]) ? $Format["SerieMax"] : "Max"; + $SerieMedian = isset($Format["SerieMedian"]) ? $Format["SerieMedian"] : null; + $LineWidth = isset($Format["LineWidth"]) ? $Format["LineWidth"] : 1; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 0; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 0; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 0; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; + $ExtremityWidth = isset($Format["ExtremityWidth"]) ? $Format["ExtremityWidth"] : 1; + $ExtremityLength = isset($Format["ExtremityLength"]) ? $Format["ExtremityLength"] : 3; + $ExtremityR = isset($Format["ExtremityR"]) ? $Format["ExtremityR"] : 0; + $ExtremityG = isset($Format["ExtremityG"]) ? $Format["ExtremityG"] : 0; + $ExtremityB = isset($Format["ExtremityB"]) ? $Format["ExtremityB"] : 0; + $ExtremityAlpha = isset($Format["ExtremityAlpha"]) ? $Format["ExtremityAlpha"] : 100; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 8; + $BoxUpR = isset($Format["BoxUpR"]) ? $Format["BoxUpR"] : 188; + $BoxUpG = isset($Format["BoxUpG"]) ? $Format["BoxUpG"] : 224; + $BoxUpB = isset($Format["BoxUpB"]) ? $Format["BoxUpB"] : 46; + $BoxUpAlpha = isset($Format["BoxUpAlpha"]) ? $Format["BoxUpAlpha"] : 100; + $BoxUpSurrounding = isset($Format["BoxUpSurrounding"]) ? $Format["BoxUpSurrounding"] : null; + $BoxUpBorderR = isset($Format["BoxUpBorderR"]) ? $Format["BoxUpBorderR"] : $BoxUpR - 20; + $BoxUpBorderG = isset($Format["BoxUpBorderG"]) ? $Format["BoxUpBorderG"] : $BoxUpG - 20; + $BoxUpBorderB = isset($Format["BoxUpBorderB"]) ? $Format["BoxUpBorderB"] : $BoxUpB - 20; + $BoxUpBorderAlpha = isset($Format["BoxUpBorderAlpha"]) ? $Format["BoxUpBorderAlpha"] : 100; + $BoxDownR = isset($Format["BoxDownR"]) ? $Format["BoxDownR"] : 224; + $BoxDownG = isset($Format["BoxDownG"]) ? $Format["BoxDownG"] : 100; + $BoxDownB = isset($Format["BoxDownB"]) ? $Format["BoxDownB"] : 46; + $BoxDownAlpha = isset($Format["BoxDownAlpha"]) ? $Format["BoxDownAlpha"] : 100; + $BoxDownSurrounding = isset($Format["BoxDownSurrounding"]) ? $Format["BoxDownSurrounding"] : null; + $BoxDownBorderR = isset($Format["BoxDownBorderR"]) ? $Format["BoxDownBorderR"] : $BoxDownR - 20; + $BoxDownBorderG = isset($Format["BoxDownBorderG"]) ? $Format["BoxDownBorderG"] : $BoxDownG - 20; + $BoxDownBorderB = isset($Format["BoxDownBorderB"]) ? $Format["BoxDownBorderB"] : $BoxDownB - 20; + $BoxDownBorderAlpha = isset($Format["BoxDownBorderAlpha"]) ? $Format["BoxDownBorderAlpha"] : 100; + $ShadowOnBoxesOnly = isset($Format["ShadowOnBoxesOnly"]) ? $Format["ShadowOnBoxesOnly"] : true; + $MedianR = isset($Format["MedianR"]) ? $Format["MedianR"] : 255; + $MedianG = isset($Format["MedianG"]) ? $Format["MedianG"] : 0; + $MedianB = isset($Format["MedianB"]) ? $Format["MedianB"] : 0; + $MedianAlpha = isset($Format["MedianAlpha"]) ? $Format["MedianAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : "Stock Chart"; + + /* Data Processing */ + if ($BoxUpSurrounding != null) { + $BoxUpBorderR = $BoxUpR + $BoxUpSurrounding; + $BoxUpBorderG = $BoxUpG + $BoxUpSurrounding; + $BoxUpBorderB = $BoxUpB + $BoxUpSurrounding; + } + if ($BoxDownSurrounding != null) { + $BoxDownBorderR = $BoxDownR + $BoxDownSurrounding; + $BoxDownBorderG = $BoxDownG + $BoxDownSurrounding; + $BoxDownBorderB = $BoxDownB + $BoxDownSurrounding; + } + + if ($LineWidth != 1) { + $LineOffset = $LineWidth / 2; + } + $BoxOffset = $BoxWidth / 2; + + $Data = $this->pChartObject->DataSet->getData(); + list($XMargin, $XDivs) = $this->pChartObject->scaleGetXSettings(); + + if (!isset($Data["Series"][$SerieOpen]) + || !isset($Data["Series"][$SerieClose]) + || !isset($Data["Series"][$SerieMin]) + || !isset($Data["Series"][$SerieMax]) + ) { + return STOCK_MISSING_SERIE; + } + $Plots = []; + foreach ($Data["Series"][$SerieOpen]["Data"] as $Key => $Value) { + $Point = []; + if (isset($Data["Series"][$SerieClose]["Data"][$Key]) + || isset($Data["Series"][$SerieMin]["Data"][$Key]) + || isset($Data["Series"][$SerieMax]["Data"][$Key]) + ) { + $Point = [ + $Value, + $Data["Series"][$SerieClose]["Data"][$Key], + $Data["Series"][$SerieMin]["Data"][$Key], + $Data["Series"][$SerieMax]["Data"][$Key] + ]; + } + if ($SerieMedian != null && isset($Data["Series"][$SerieMedian]["Data"][$Key])) { + $Point[] = $Data["Series"][$SerieMedian]["Data"][$Key]; + } + $Plots[] = $Point; + } + + $AxisID = $Data["Series"][$SerieOpen]["Axis"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + + $YZero = $this->pChartObject->scaleComputeY(0, ["AxisID" => $AxisID]); + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; + + $X = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $this->pChartObject->GraphAreaY1 + $XMargin; + + $LineSettings = ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha]; + $ExtremitySettings = [ + "R" => $ExtremityR, + "G" => $ExtremityG, + "B" => $ExtremityB, + "Alpha" => $ExtremityAlpha + ]; + $BoxUpSettings = [ + "R" => $BoxUpR, + "G" => $BoxUpG, + "B" => $BoxUpB, + "Alpha" => $BoxUpAlpha, + "BorderR" => $BoxUpBorderR, + "BorderG" => $BoxUpBorderG, + "BorderB" => $BoxUpBorderB, + "BorderAlpha" => $BoxUpBorderAlpha + ]; + $BoxDownSettings = [ + "R" => $BoxDownR, + "G" => $BoxDownG, + "B" => $BoxDownB, + "Alpha" => $BoxDownAlpha, + "BorderR" => $BoxDownBorderR, + "BorderG" => $BoxDownBorderG, + "BorderB" => $BoxDownBorderB, + "BorderAlpha" => $BoxDownBorderAlpha + ]; + $MedianSettings = ["R" => $MedianR, "G" => $MedianG, "B" => $MedianB, "Alpha" => $MedianAlpha]; + + foreach ($Plots as $Key => $Points) { + $PosArray = $this->pChartObject->scaleComputeY($Points, ["AxisID" => $AxisID]); + + $Values = "Open :" . $Data["Series"][$SerieOpen]["Data"][$Key] + . "
Close : " . $Data["Series"][$SerieClose]["Data"][$Key] + . "
Min : " . $Data["Series"][$SerieMin]["Data"][$Key] + . "
Max : " . $Data["Series"][$SerieMax]["Data"][$Key] . "
"; + if ($SerieMedian != null) { + $Values = $Values . "Median : " . $Data["Series"][$SerieMedian]["Data"][$Key] . "
"; + } + if ($PosArray[0] > $PosArray[1]) { + $ImageMapColor = $this->pChartObject->toHTMLColor($BoxUpR, $BoxUpG, $BoxUpB); + } else { + $ImageMapColor = $this->pChartObject->toHTMLColor($BoxDownR, $BoxDownG, $BoxDownB); + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero > $this->pChartObject->GraphAreaY2 - 1) { + $YZero = $this->pChartObject->GraphAreaY2 - 1; + } + if ($YZero < $this->pChartObject->GraphAreaY1 + 1) { + $YZero = $this->pChartObject->GraphAreaY1 + 1; + } + + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) + / $XDivs + ; + } + + if ($ShadowOnBoxesOnly) { + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + } + + if ($LineWidth == 1) { + $this->pChartObject->drawLine($X, $PosArray[2], $X, $PosArray[3], $LineSettings); + } else { + $this->pChartObject->drawFilledRectangle( + $X - $LineOffset, + $PosArray[2], + $X + $LineOffset, + $PosArray[3], + $LineSettings + ); + } + + if ($ExtremityWidth == 1) { + $this->pChartObject->drawLine( + $X - $ExtremityLength, + $PosArray[2], + $X + $ExtremityLength, + $PosArray[2], + $ExtremitySettings + ); + $this->pChartObject->drawLine( + $X - $ExtremityLength, + $PosArray[3], + $X + $ExtremityLength, + $PosArray[3], + $ExtremitySettings + ); + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X - $ExtremityLength), + floor($PosArray[2]), + floor($X + $ExtremityLength), + floor($PosArray[3]) + ), + $ImageMapColor, + $ImageMapTitle, + $Values + ); + } + } else { + $this->pChartObject->drawFilledRectangle( + $X - $ExtremityLength, + $PosArray[2], + $X + $ExtremityLength, + $PosArray[2] - $ExtremityWidth, + $ExtremitySettings + ); + $this->pChartObject->drawFilledRectangle( + $X - $ExtremityLength, + $PosArray[3], + $X + $ExtremityLength, + $PosArray[3] + $ExtremityWidth, + $ExtremitySettings + ); + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X - $ExtremityLength), + floor($PosArray[2] - $ExtremityWidth), + floor($X + $ExtremityLength), + floor($PosArray[3] + $ExtremityWidth) + ), + $ImageMapColor, + $ImageMapTitle, + $Values + ); + } + } + + if ($ShadowOnBoxesOnly) { + $this->pChartObject->Shadow = $RestoreShadow; + } + + if ($PosArray[0] > $PosArray[1]) { + $this->pChartObject->drawFilledRectangle( + $X - $BoxOffset, + $PosArray[0], + $X + $BoxOffset, + $PosArray[1], + $BoxUpSettings + ); + } else { + $this->pChartObject->drawFilledRectangle( + $X - $BoxOffset, + $PosArray[0], + $X + $BoxOffset, + $PosArray[1], + $BoxDownSettings + ); + } + + if (isset($PosArray[4])) { + $this->pChartObject->drawLine( + $X - $ExtremityLength, + $PosArray[4], + $X + $ExtremityLength, + $PosArray[4], + $MedianSettings + ); + } + $X = $X + $XStep; + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + if ($YZero > $this->pChartObject->GraphAreaX2 - 1) { + $YZero = $this->pChartObject->GraphAreaX2 - 1; + } + if ($YZero < $this->pChartObject->GraphAreaX1 + 1) { + $YZero = $this->pChartObject->GraphAreaX1 + 1; + } + + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $XMargin * 2) + / $XDivs + ; + } + + if ($LineWidth == 1) { + $this->pChartObject->drawLine($PosArray[2], $Y, $PosArray[3], $Y, $LineSettings); + } else { + $this->pChartObject->drawFilledRectangle( + $PosArray[2], + $Y - $LineOffset, + $PosArray[3], + $Y + $LineOffset, + $LineSettings + ); + } + if ($ShadowOnBoxesOnly) { + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = false; + } + + if ($ExtremityWidth == 1) { + $this->pChartObject->drawLine( + $PosArray[2], + $Y - $ExtremityLength, + $PosArray[2], + $Y + $ExtremityLength, + $ExtremitySettings + ); + $this->pChartObject->drawLine( + $PosArray[3], + $Y - $ExtremityLength, + $PosArray[3], + $Y + $ExtremityLength, + $ExtremitySettings + ); + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($PosArray[2]), + floor($Y - $ExtremityLength), + floor($PosArray[3]), + floor($Y + $ExtremityLength) + ), + $ImageMapColor, + $ImageMapTitle, + $Values + ); + } + } else { + $this->pChartObject->drawFilledRectangle( + $PosArray[2], + $Y - $ExtremityLength, + $PosArray[2] - $ExtremityWidth, + $Y + $ExtremityLength, + $ExtremitySettings + ); + $this->pChartObject->drawFilledRectangle( + $PosArray[3], + $Y - $ExtremityLength, + $PosArray[3] + $ExtremityWidth, + $Y + $ExtremityLength, + $ExtremitySettings + ); + + if ($RecordImageMap) { + $this->pChartObject->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($PosArray[2] - $ExtremityWidth), + floor($Y - $ExtremityLength), + floor($PosArray[3] + $ExtremityWidth), + floor($Y + $ExtremityLength) + ), + $ImageMapColor, + $ImageMapTitle, + $Values + ); + } + } + + if ($ShadowOnBoxesOnly) { + $this->pChartObject->Shadow = $RestoreShadow; + } + + if ($PosArray[0] < $PosArray[1]) { + $this->pChartObject->drawFilledRectangle( + $PosArray[0], + $Y - $BoxOffset, + $PosArray[1], + $Y + $BoxOffset, + $BoxUpSettings + ); + } else { + $this->pChartObject->drawFilledRectangle( + $PosArray[0], + $Y - $BoxOffset, + $PosArray[1], + $Y + $BoxOffset, + $BoxDownSettings + ); + } + if (isset($PosArray[4])) { + $this->pChartObject->drawLine( + $PosArray[4], + $Y - $ExtremityLength, + $PosArray[4], + $Y + $ExtremityLength, + $MedianSettings + ); + } + $Y = $Y + $XStep; + } + } + } +} diff --git a/vendor/szymach/c-pchart/src/Chart/Surface.php b/vendor/szymach/c-pchart/src/Chart/Surface.php new file mode 100644 index 0000000..b38e46c --- /dev/null +++ b/vendor/szymach/c-pchart/src/Chart/Surface.php @@ -0,0 +1,414 @@ +pChartObject = $pChartObject; + } + + /** + * Define the grid size and initialise the 2D matrix + * @param int $XSize + * @param int $YSize + */ + public function setGrid($XSize = 10, $YSize = 10) + { + for ($X = 0; $X <= $XSize; $X++) { + for ($Y = 0; $Y <= $YSize; $Y++) { + $this->Points[$X][$Y] = UNKNOWN; + } + } + + $this->GridSizeX = $XSize; + $this->GridSizeY = $YSize; + } + + /** + * Add a point on the grid + * @param int $X + * @param int $Y + * @param int|float $Value + * @param boolean $Force + * @return null + */ + public function addPoint($X, $Y, $Value, $Force = true) + { + if ($X < 0 || $X > $this->GridSizeX) { + return 0; + } + if ($Y < 0 || $Y > $this->GridSizeY) { + return 0; + } + + if ($this->Points[$X][$Y] == UNKNOWN || $Force) { + $this->Points[$X][$Y] = $Value; + } elseif ($this->Points[$X][$Y] == UNKNOWN) { + $this->Points[$X][$Y] = $Value; + } else { + $this->Points[$X][$Y] = ($this->Points[$X][$Y] + $Value) / 2; + } + } + + /** + * Write the X labels + * @param array $Format + * @return null|int + */ + public function writeXLabels(array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; + $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_TOP; + $Labels = isset($Format["Labels"]) ? $Format["Labels"] : null; + $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; + + if ($Labels != null && !is_array($Labels)) { + $Label = $Labels; + $Labels = [$Label]; + } + + $X0 = $this->pChartObject->GraphAreaX1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX + 1); + + $Settings = ["Angle" => $Angle, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + if ($Position == LABEL_POSITION_TOP) { + $YPos = $this->pChartObject->GraphAreaY1 - $Padding; + if ($Angle == 0) { + $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + } + if ($Angle != 0) { + $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; + } + } elseif ($Position == LABEL_POSITION_BOTTOM) { + $YPos = $this->pChartObject->GraphAreaY2 + $Padding; + if ($Angle == 0) { + $Settings["Align"] = TEXT_ALIGN_TOPMIDDLE; + } + if ($Angle != 0) { + $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; + } + } else { + return -1; + } + for ($X = 0; $X <= $this->GridSizeX; $X++) { + $XPos = floor($X0 + $X * $XSize + $XSize / 2); + + if ($Labels == null || !isset($Labels[$X])) { + $Value = $X + $CountOffset; + } else { + $Value = $Labels[$X]; + } + $this->pChartObject->drawText($XPos, $YPos, $Value, $Settings); + } + } + + /** + * Write the Y labels + * @param array $Format + * @return type + */ + public function writeYLabels(array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; + $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_LEFT; + $Labels = isset($Format["Labels"]) ? $Format["Labels"] : null; + $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; + + if ($Labels != null && !is_array($Labels)) { + $Label = $Labels; + $Labels = [$Label]; + } + + $Y0 = $this->pChartObject->GraphAreaY1; + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY + 1); + + $Settings = ["Angle" => $Angle, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + if ($Position == LABEL_POSITION_LEFT) { + $XPos = $this->pChartObject->GraphAreaX1 - $Padding; + $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; + } elseif ($Position == LABEL_POSITION_RIGHT) { + $XPos = $this->pChartObject->GraphAreaX2 + $Padding; + $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; + } else { + return -1; + } + for ($Y = 0; $Y <= $this->GridSizeY; $Y++) { + $YPos = floor($Y0 + $Y * $YSize + $YSize / 2); + + if ($Labels == null || !isset($Labels[$Y])) { + $Value = $Y + $CountOffset; + } else { + $Value = $Labels[$Y]; + } + $this->pChartObject->drawText($XPos, $YPos, $Value, $Settings); + } + } + + /** + * Draw the area arround the specified Threshold + * @param int|float $Threshold + * @param array $Format + */ + public function drawContour($Threshold, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 0; + + $X0 = $this->pChartObject->GraphAreaX1; + $Y0 = $this->pChartObject->GraphAreaY1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX + 1); + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY + 1); + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]; + + for ($X = 0; $X <= $this->GridSizeX; $X++) { + for ($Y = 0; $Y <= $this->GridSizeY; $Y++) { + $Value = $this->Points[$X][$Y]; + + if ($Value != UNKNOWN && $Value != IGNORED && $Value >= $Threshold) { + $X1 = floor($X0 + $X * $XSize) + $Padding; + $Y1 = floor($Y0 + $Y * $YSize) + $Padding; + $X2 = floor($X0 + $X * $XSize + $XSize); + $Y2 = floor($Y0 + $Y * $YSize + $YSize); + + if ($X > 0 && $this->Points[$X - 1][$Y] != UNKNOWN + && $this->Points[$X - 1][$Y] != IGNORED + && $this->Points[$X - 1][$Y] < $Threshold + ) { + $this->pChartObject->drawLine($X1, $Y1, $X1, $Y2, $Color); + } + if ($Y > 0 && $this->Points[$X][$Y - 1] != UNKNOWN + && $this->Points[$X][$Y - 1] != IGNORED + && $this->Points[$X][$Y - 1] < $Threshold + ) { + $this->pChartObject->drawLine($X1, $Y1, $X2, $Y1, $Color); + } + if ($X < $this->GridSizeX + && $this->Points[$X + 1][$Y] != UNKNOWN + && $this->Points[$X + 1][$Y] != IGNORED + && $this->Points[$X + 1][$Y] < $Threshold + ) { + $this->pChartObject->drawLine($X2, $Y1, $X2, $Y2, $Color); + } + if ($Y < $this->GridSizeY + && $this->Points[$X][$Y + 1] != UNKNOWN + && $this->Points[$X][$Y + 1] != IGNORED + && $this->Points[$X][$Y + 1] < $Threshold + ) { + $this->pChartObject->drawLine($X1, $Y2, $X2, $Y2, $Color); + } + } + } + } + } + + /** + * Draw the surface chart + * @param array $Format + */ + public function drawSurface(array $Format = []) + { + $Palette = isset($Format["Palette"]) ? $Format["Palette"] : null; + $ShadeR1 = isset($Format["ShadeR1"]) ? $Format["ShadeR1"] : 77; + $ShadeG1 = isset($Format["ShadeG1"]) ? $Format["ShadeG1"] : 205; + $ShadeB1 = isset($Format["ShadeB1"]) ? $Format["ShadeB1"] : 21; + $ShadeA1 = isset($Format["ShadeA1"]) ? $Format["ShadeA1"] : 40; + $ShadeR2 = isset($Format["ShadeR2"]) ? $Format["ShadeR2"] : 227; + $ShadeG2 = isset($Format["ShadeG2"]) ? $Format["ShadeG2"] : 135; + $ShadeB2 = isset($Format["ShadeB2"]) ? $Format["ShadeB2"] : 61; + $ShadeA2 = isset($Format["ShadeA2"]) ? $Format["ShadeA2"] : 100; + $Border = isset($Format["Border"]) ? $Format["Border"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : -1; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 1; + + $X0 = $this->pChartObject->GraphAreaX1; + $Y0 = $this->pChartObject->GraphAreaY1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX + 1); + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY + 1); + + for ($X = 0; $X <= $this->GridSizeX; $X++) { + for ($Y = 0; $Y <= $this->GridSizeY; $Y++) { + $Value = $this->Points[$X][$Y]; + + if ($Value != UNKNOWN && $Value != IGNORED) { + $X1 = floor($X0 + $X * $XSize) + $Padding; + $Y1 = floor($Y0 + $Y * $YSize) + $Padding; + $X2 = floor($X0 + $X * $XSize + $XSize); + $Y2 = floor($Y0 + $Y * $YSize + $YSize); + + if ($Palette != null) { + if (isset($Palette[$Value]) && isset($Palette[$Value]["R"])) { + $R = $Palette[$Value]["R"]; + } else { + $R = 0; + } + + if (isset($Palette[$Value]) && isset($Palette[$Value]["G"])) { + $G = $Palette[$Value]["G"]; + } else { + $G = 0; + } + + if (isset($Palette[$Value]) && isset($Palette[$Value]["B"])) { + $B = $Palette[$Value]["B"]; + } else { + $B = 0; + } + if (isset($Palette[$Value]) && isset($Palette[$Value]["Alpha"])) { + $Alpha = $Palette[$Value]["Alpha"]; + } else { + $Alpha = 1000; + } + } else { + $R = (($ShadeR2 - $ShadeR1) / 100) * $Value + $ShadeR1; + $G = (($ShadeG2 - $ShadeG1) / 100) * $Value + $ShadeG1; + $B = (($ShadeB2 - $ShadeB1) / 100) * $Value + $ShadeB1; + $Alpha = (($ShadeA2 - $ShadeA1) / 100) * $Value + $ShadeA1; + } + + $Settings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + if ($Border) { + $Settings["BorderR"] = $BorderR; + $Settings["BorderG"] = $BorderG; + $Settings["BorderB"] = $BorderB; + } + if ($Surrounding != -1) { + $Settings["BorderR"] = $R + $Surrounding; + $Settings["BorderG"] = $G + $Surrounding; + $Settings["BorderB"] = $B + $Surrounding; + } + + $this->pChartObject->drawFilledRectangle($X1, $Y1, $X2 - 1, $Y2 - 1, $Settings); + } + } + } + } + + /** + * Compute the missing points + */ + public function computeMissing() + { + $Missing = []; + for ($X = 0; $X <= $this->GridSizeX; $X++) { + for ($Y = 0; $Y <= $this->GridSizeY; $Y++) { + if ($this->Points[$X][$Y] == UNKNOWN) { + $Missing[] = $X . "," . $Y; + } + } + } + shuffle($Missing); + + foreach ($Missing as $Pos) { + $Pos = preg_split("/,/", $Pos); + $X = $Pos[0]; + $Y = $Pos[1]; + + if ($this->Points[$X][$Y] == UNKNOWN) { + $NearestNeighbor = $this->getNearestNeighbor($X, $Y); + + $Value = 0; + $Points = 0; + for ($Xi = $X - $NearestNeighbor; $Xi <= $X + $NearestNeighbor; $Xi++) { + for ($Yi = $Y - $NearestNeighbor; $Yi <= $Y + $NearestNeighbor; $Yi++) { + if ($Xi >= 0 + && $Yi >= 0 + && $Xi <= $this->GridSizeX + && $Yi <= $this->GridSizeY + && $this->Points[$Xi][$Yi] != UNKNOWN + && $this->Points[$Xi][$Yi] != IGNORED + ) { + $Value = $Value + $this->Points[$Xi][$Yi]; + $Points++; + } + } + } + + if ($Points != 0) { + $this->Points[$X][$Y] = $Value / $Points; + } + } + } + } + + /** + * Return the nearest Neighbor distance of a point + * @param int $Xp + * @param int $Yp + * @return int + */ + public function getNearestNeighbor($Xp, $Yp) + { + $Nearest = UNKNOWN; + for ($X = 0; $X <= $this->GridSizeX; $X++) { + for ($Y = 0; $Y <= $this->GridSizeY; $Y++) { + if ($this->Points[$X][$Y] != UNKNOWN && $this->Points[$X][$Y] != IGNORED) { + $DistanceX = max($Xp, $X) - min($Xp, $X); + $DistanceY = max($Yp, $Y) - min($Yp, $Y); + $Distance = max($DistanceX, $DistanceY); + if ($Distance < $Nearest || $Nearest == UNKNOWN) { + $Nearest = $Distance; + } + } + } + } + return $Nearest; + } +} diff --git a/vendor/szymach/c-pchart/src/Data.php b/vendor/szymach/c-pchart/src/Data.php new file mode 100644 index 0000000..6254589 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Data.php @@ -0,0 +1,1318 @@ + ["R" => 188, "G" => 224, "B" => 46, "Alpha" => 100], + "1" => ["R" => 224, "G" => 100, "B" => 46, "Alpha" => 100], + "2" => ["R" => 224, "G" => 214, "B" => 46, "Alpha" => 100], + "3" => ["R" => 46, "G" => 151, "B" => 224, "Alpha" => 100], + "4" => ["R" => 176, "G" => 46, "B" => 224, "Alpha" => 100], + "5" => ["R" => 224, "G" => 46, "B" => 117, "Alpha" => 100], + "6" => ["R" => 92, "G" => 224, "B" => 46, "Alpha" => 100], + "7" => ["R" => 224, "G" => 176, "B" => 46, "Alpha" => 100] + ]; + + public function __construct() + { + $this->Data["XAxisDisplay"] = AXIS_FORMAT_DEFAULT; + $this->Data["XAxisFormat"] = null; + $this->Data["XAxisName"] = null; + $this->Data["XAxisUnit"] = null; + $this->Data["Abscissa"] = null; + $this->Data["AbsicssaPosition"] = AXIS_POSITION_BOTTOM; + + $this->Data["Axis"][0]["Display"] = AXIS_FORMAT_DEFAULT; + $this->Data["Axis"][0]["Position"] = AXIS_POSITION_LEFT; + $this->Data["Axis"][0]["Identity"] = AXIS_Y; + } + + /** + * Add a single point or an array to the given serie + * @param mixed $Values + * @param string $SerieName + * @return int + */ + public function addPoints($Values, $SerieName = "Serie1") + { + if (!isset($this->Data["Series"][$SerieName])) { + $this->initialise($SerieName); + } + if (is_array($Values)) { + foreach ($Values as $Value) { + $this->Data["Series"][$SerieName]["Data"][] = $Value; + } + } else { + $this->Data["Series"][$SerieName]["Data"][] = $Values; + } + + if ($Values != VOID) { + $StrippedData = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]); + if (empty($StrippedData)) { + $this->Data["Series"][$SerieName]["Max"] = 0; + $this->Data["Series"][$SerieName]["Min"] = 0; + return 0; + } + $this->Data["Series"][$SerieName]["Max"] = max($StrippedData); + $this->Data["Series"][$SerieName]["Min"] = min($StrippedData); + } + } + + /** + * Strip VOID values + * @param mixed $Values + * @return array + */ + public function stripVOID($Values) + { + if (!is_array($Values)) { + return []; + } + $Result = []; + foreach ($Values as $Value) { + if ($Value != VOID) { + $Result[] = $Value; + } + } + return $Result; + } + + /** + * Return the number of values contained in a given serie + * @param string $Serie + * @return int + */ + public function getSerieCount($Serie) + { + if (isset($this->Data["Series"][$Serie]["Data"])) { + return sizeof($this->Data["Series"][$Serie]["Data"]); + } + return 0; + } + + /** + * Remove a serie from the pData object + * @param mixed $Series + */ + public function removeSerie($Series) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + unset($this->Data["Series"][$Serie]); + } + } + } + + /** + * Return a value from given serie & index + * @param string $Serie + * @param int $Index + * @return mixed + */ + public function getValueAt($Serie, $Index = 0) + { + if (isset($this->Data["Series"][$Serie]["Data"][$Index])) { + return $this->Data["Series"][$Serie]["Data"][$Index]; + } + return null; + } + + /** + * Return the values array + * @param string $Serie + * @return mixed + */ + public function getValues($Serie) + { + if (isset($this->Data["Series"][$Serie]["Data"])) { + return $this->Data["Series"][$Serie]["Data"]; + } + return null; + } + + /** + * Reverse the values in the given serie + * @param mixed $Series + */ + public function reverseSerie($Series) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie]["Data"])) { + $this->Data["Series"][$Serie]["Data"] = array_reverse( + $this->Data["Series"][$Serie]["Data"] + ); + } + } + } + + /** + * Return the sum of the serie values + * @param string $Serie + * @return int|null + */ + public function getSum($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + return array_sum($this->Data["Series"][$Serie]["Data"]); + } + return null; + } + + /** + * Return the max value of a given serie + * @param string $Serie + * @return mixed + */ + public function getMax($Serie) + { + if (isset($this->Data["Series"][$Serie]["Max"])) { + return $this->Data["Series"][$Serie]["Max"]; + } + return null; + } + + /** + * @param string $Serie + * @return mixed + */ + public function getMin($Serie) + { + if (isset($this->Data["Series"][$Serie]["Min"])) { + return $this->Data["Series"][$Serie]["Min"]; + } + return null; + } + + /** + * Set the description of a given serie + * @param mixed $Series + * @param string $Shape + */ + public function setSerieShape($Series, $Shape = SERIE_SHAPE_FILLEDCIRCLE) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["Shape"] = $Shape; + } + } + } + + /** + * Set the description of a given serie + * @param string|array $Series + * @param string $Description + */ + public function setSerieDescription($Series, $Description = "My serie") + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["Description"] = $Description; + } + } + } + + /** + * Set a serie as "drawable" while calling a rendering public function + * @param string|array $Series + * @param boolean $Drawable + */ + public function setSerieDrawable($Series, $Drawable = true) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["isDrawable"] = $Drawable; + } + } + } + + /** + * Set the icon associated to a given serie + * @param mixed $Series + * @param mixed $Picture + */ + public function setSeriePicture($Series, $Picture = null) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["Picture"] = $Picture; + } + } + } + + /** + * Set the name of the X Axis + * @param string $Name + */ + public function setXAxisName($Name) + { + $this->Data["XAxisName"] = $Name; + } + + /** + * Set the display mode of the X Axis + * @param int $Mode + * @param array $Format + */ + public function setXAxisDisplay($Mode, $Format = null) + { + $this->Data["XAxisDisplay"] = $Mode; + $this->Data["XAxisFormat"] = $Format; + } + + /** + * Set the unit that will be displayed on the X axis + * @param string $Unit + */ + public function setXAxisUnit($Unit) + { + $this->Data["XAxisUnit"] = $Unit; + } + + /** + * Set the serie that will be used as abscissa + * @param string $Serie + */ + public function setAbscissa($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Abscissa"] = $Serie; + } + } + + /** + * Set the position of the abscissa axis + * @param int $Position + */ + public function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM) + { + $this->Data["AbsicssaPosition"] = $Position; + } + + /** + * Set the name of the abscissa axis + * @param string $Name + */ + public function setAbscissaName($Name) + { + $this->Data["AbscissaName"] = $Name; + } + + /** + * Create a scatter group specified in X and Y data series + * @param string $SerieX + * @param string $SerieY + * @param int $ID + */ + public function setScatterSerie($SerieX, $SerieY, $ID = 0) + { + if (isset($this->Data["Series"][$SerieX]) && isset($this->Data["Series"][$SerieY])) { + $this->initScatterSerie($ID); + $this->Data["ScatterSeries"][$ID]["X"] = $SerieX; + $this->Data["ScatterSeries"][$ID]["Y"] = $SerieY; + } + } + + /** + * Set the shape of a given sctatter serie + * @param int $ID + * @param int $Shape + */ + public function setScatterSerieShape($ID, $Shape = SERIE_SHAPE_FILLEDCIRCLE) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Shape"] = $Shape; + } + } + + /** + * Set the description of a given scatter serie + * @param int $ID + * @param string $Description + */ + public function setScatterSerieDescription($ID, $Description = "My serie") + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Description"] = $Description; + } + } + + /** + * Set the icon associated to a given scatter serie + * @param int $ID + * @param mixed $Picture + */ + public function setScatterSeriePicture($ID, $Picture = null) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Picture"] = $Picture; + } + } + + /** + * Set a scatter serie as "drawable" while calling a rendering public function + * @param int $ID + * @param boolean $Drawable + */ + public function setScatterSerieDrawable($ID, $Drawable = true) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["isDrawable"] = $Drawable; + } + } + + /** + * Define if a scatter serie should be draw with ticks + * @param int $ID + * @param int $Width + */ + public function setScatterSerieTicks($ID, $Width = 0) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Ticks"] = $Width; + } + } + + /** + * Define if a scatter serie should be draw with a special weight + * @param int $ID + * @param int $Weight + */ + public function setScatterSerieWeight($ID, $Weight = 0) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Weight"] = $Weight; + } + } + + /** + * Associate a color to a scatter serie + * @param int $ID + * @param array $Format + */ + public function setScatterSerieColor($ID, array $Format) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if (isset($this->Data["ScatterSeries"][$ID])) { + $this->Data["ScatterSeries"][$ID]["Color"]["R"] = $R; + $this->Data["ScatterSeries"][$ID]["Color"]["G"] = $G; + $this->Data["ScatterSeries"][$ID]["Color"]["B"] = $B; + $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = $Alpha; + } + } + + /** + * Compute the series limits for an individual and global point of view + * @return array + */ + public function limits() + { + $GlobalMin = ABSOLUTE_MAX; + $GlobalMax = ABSOLUTE_MIN; + + foreach (array_keys($this->Data["Series"]) as $Key) { + if ($this->Data["Abscissa"] != $Key + && $this->Data["Series"][$Key]["isDrawable"] == true + ) { + if ($GlobalMin > $this->Data["Series"][$Key]["Min"]) { + $GlobalMin = $this->Data["Series"][$Key]["Min"]; + } + if ($GlobalMax < $this->Data["Series"][$Key]["Max"]) { + $GlobalMax = $this->Data["Series"][$Key]["Max"]; + } + } + } + $this->Data["Min"] = $GlobalMin; + $this->Data["Max"] = $GlobalMax; + + return [$GlobalMin, $GlobalMax]; + } + + /** + * Mark all series as drawable + */ + public function drawAll() + { + foreach (array_keys($this->Data["Series"]) as $Key) { + if ($this->Data["Abscissa"] != $Key) { + $this->Data["Series"][$Key]["isDrawable"] = true; + } + } + } + + /** + * Return the average value of the given serie + * @param string $Serie + * @return int|null + */ + public function getSerieAverage($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + return array_sum($SerieData) / sizeof($SerieData); + } + + return null; + } + + /** + * Return the geometric mean of the given serie + * @param string $Serie + * @return int|null + */ + public function getGeometricMean($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + $Seriesum = 1; + foreach ($SerieData as $Value) { + $Seriesum = $Seriesum * $Value; + } + return pow($Seriesum, 1 / sizeof($SerieData)); + } + + return null; + } + + /** + * Return the harmonic mean of the given serie + * @param string $Serie + * @return int|null + */ + public function getHarmonicMean($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + $Seriesum = 0; + foreach ($SerieData as $Value) { + $Seriesum = $Seriesum + 1 / $Value; + } + return sizeof($SerieData) / $Seriesum; + } + + return null; + } + + /** + * Return the standard deviation of the given serie + * @param string $Serie + * @return double|null + */ + public function getStandardDeviation($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $Average = $this->getSerieAverage($Serie); + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + + $DeviationSum = 0; + foreach ($SerieData as $Key => $Value) { + $DeviationSum = $DeviationSum + ($Value - $Average) * ($Value - $Average); + } + return sqrt($DeviationSum / count($SerieData)); + } + return null; + } + + /** + * Return the Coefficient of variation of the given serie + * @param string $Serie + * @return float|null + */ + public function getCoefficientOfVariation($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $Average = $this->getSerieAverage($Serie); + $StandardDeviation = $this->getStandardDeviation($Serie); + + if ($StandardDeviation != 0) { + return $StandardDeviation / $Average; + } + } + return null; + } + + /** + * Return the median value of the given serie + * @param string $Serie + * @return int|float + */ + public function getSerieMedian($Serie) + { + if (isset($this->Data["Series"][$Serie])) { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + sort($SerieData); + $SerieCenter = floor(sizeof($SerieData) / 2); + + if (isset($SerieData[$SerieCenter])) { + return $SerieData[$SerieCenter]; + } + } + return null; + } + + /** + * Return the x th percentil of the given serie + * @param string $Serie + * @param int $Percentil + * @return int|float| null + */ + public function getSeriePercentile($Serie = "Serie1", $Percentil = 95) + { + if (!isset($this->Data["Series"][$Serie]["Data"])) { + return null; + } + + $Values = count($this->Data["Series"][$Serie]["Data"]) - 1; + if ($Values < 0) { + $Values = 0; + } + + $PercentilID = floor(($Values / 100) * $Percentil + .5); + $SortedValues = $this->Data["Series"][$Serie]["Data"]; + sort($SortedValues); + + if (is_numeric($SortedValues[$PercentilID])) { + return $SortedValues[$PercentilID]; + } + return null; + } + + /** + * Add random values to a given serie + * @param string $SerieName + * @param array $Options + */ + public function addRandomValues($SerieName = "Serie1", array $Options = []) + { + $Values = isset($Options["Values"]) ? $Options["Values"] : 20; + $Min = isset($Options["Min"]) ? $Options["Min"] : 0; + $Max = isset($Options["Max"]) ? $Options["Max"] : 100; + $withFloat = isset($Options["withFloat"]) ? $Options["withFloat"] : false; + + for ($i = 0; $i <= $Values; $i++) { + $Value = $withFloat ? rand($Min * 100, $Max * 100) / 100 : rand($Min, $Max); + $this->addPoints($Value, $SerieName); + } + } + + /** + * Test if we have valid data + * @return boolean|null + */ + public function containsData() + { + if (!isset($this->Data["Series"])) { + return false; + } + + foreach (array_keys($this->Data["Series"]) as $Key) { + if ($this->Data["Abscissa"] != $Key + && $this->Data["Series"][$Key]["isDrawable"] == true + ) { + return true; + } + } + + return null; + } + + /** + * Set the display mode of an Axis + * @param int $AxisID + * @param int $Mode + * @param array $Format + */ + public function setAxisDisplay($AxisID, $Mode = AXIS_FORMAT_DEFAULT, $Format = null) + { + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Display"] = $Mode; + if ($Format != null) { + $this->Data["Axis"][$AxisID]["Format"] = $Format; + } + } + } + + /** + * Set the position of an Axis + * @param int $AxisID + * @param int $Position + */ + public function setAxisPosition($AxisID, $Position = AXIS_POSITION_LEFT) + { + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Position"] = $Position; + } + } + + /** + * Associate an unit to an axis + * @param int $AxisID + * @param string $Unit + */ + public function setAxisUnit($AxisID, $Unit) + { + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Unit"] = $Unit; + } + } + + /** + * Associate a name to an axis + * @param int $AxisID + * @param string $Name + */ + public function setAxisName($AxisID, $Name) + { + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Name"] = $Name; + } + } + + /** + * Associate a color to an axis + * @param int $AxisID + * @param array $Format + */ + public function setAxisColor($AxisID, array $Format) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Color"]["R"] = $R; + $this->Data["Axis"][$AxisID]["Color"]["G"] = $G; + $this->Data["Axis"][$AxisID]["Color"]["B"] = $B; + $this->Data["Axis"][$AxisID]["Color"]["Alpha"] = $Alpha; + } + } + + /** + * Design an axis as X or Y member + * @param int $AxisID + * @param int $Identity + */ + public function setAxisXY($AxisID, $Identity = AXIS_Y) + { + if (isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Identity"] = $Identity; + } + } + + /** + * Associate one data serie with one axis + * @param mixed $Series + * @param int $AxisID + */ + public function setSerieOnAxis($Series, $AxisID) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + $PreviousAxis = $this->Data["Series"][$Serie]["Axis"]; + + /* Create missing axis */ + if (!isset($this->Data["Axis"][$AxisID])) { + $this->Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; + $this->Data["Axis"][$AxisID]["Identity"] = AXIS_Y; + } + + $this->Data["Series"][$Serie]["Axis"] = $AxisID; + + /* Cleanup unused axis */ + $Found = false; + foreach ($this->Data["Series"] as $Values) { + if ($Values["Axis"] == $PreviousAxis) { + $Found = true; + } + } + if (!$Found) { + unset($this->Data["Axis"][$PreviousAxis]); + } + } + } + + /** + * Define if a serie should be draw with ticks + * @param mixed $Series + * @param int $Width + */ + public function setSerieTicks($Series, $Width = 0) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["Ticks"] = $Width; + } + } + } + + /** + * Define if a serie should be draw with a special weight + * @param mixed $Series + * @param int $Weight + */ + public function setSerieWeight($Series, $Weight = 0) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Serie) { + if (isset($this->Data["Series"][$Serie])) { + $this->Data["Series"][$Serie]["Weight"] = $Weight; + } + } + } + + /** + * Returns the palette of the given serie + * @param type $Serie + * @return null + */ + public function getSeriePalette($Serie) + { + if (!isset($this->Data["Series"][$Serie])) { + return null; + } + + $Result = []; + $Result["R"] = $this->Data["Series"][$Serie]["Color"]["R"]; + $Result["G"] = $this->Data["Series"][$Serie]["Color"]["G"]; + $Result["B"] = $this->Data["Series"][$Serie]["Color"]["B"]; + $Result["Alpha"] = $this->Data["Series"][$Serie]["Color"]["Alpha"]; + + return $Result; + } + + /** + * Set the color of one serie + * @param mixed $Series + * @param array $Format + */ + public function setPalette($Series, array $Format = []) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + + foreach ($Series as $Key => $Serie) { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if (isset($this->Data["Series"][$Serie])) { + $OldR = $this->Data["Series"][$Serie]["Color"]["R"]; + $OldG = $this->Data["Series"][$Serie]["Color"]["G"]; + $OldB = $this->Data["Series"][$Serie]["Color"]["B"]; + $this->Data["Series"][$Serie]["Color"]["R"] = $R; + $this->Data["Series"][$Serie]["Color"]["G"] = $G; + $this->Data["Series"][$Serie]["Color"]["B"] = $B; + $this->Data["Series"][$Serie]["Color"]["Alpha"] = $Alpha; + + /* Do reverse processing on the internal palette array */ + foreach ($this->Palette as $Key => $Value) { + if ($Value["R"] == $OldR && $Value["G"] == $OldG && $Value["B"] == $OldB) { + $this->Palette[$Key]["R"] = $R; + $this->Palette[$Key]["G"] = $G; + $this->Palette[$Key]["B"] = $B; + $this->Palette[$Key]["Alpha"] = $Alpha; + } + } + } + } + } + + /** + * Load a palette file + * @param string $FileName + * @param boolean $Overwrite + * @throws Exception + */ + public function loadPalette($FileName, $Overwrite = false) + { + $path = file_exists($FileName) + ? $FileName + : sprintf('%s/../resources/palettes/%s', __DIR__, ltrim($FileName, '/')) + ; + + $fileHandle = @fopen($path, "r"); + if (!$fileHandle) { + throw new Exception(sprintf( + 'The requested palette "%s" was not found at path "%s"!', + $FileName, + $path + )); + } + + if ($Overwrite) { + $this->Palette = []; + } + + while (!feof($fileHandle)) { + $line = fgets($fileHandle, 4096); + if (false === $line) { + continue; + } + $row = explode(',', $line); + if (empty($row)) { + continue; + } + if (count($row) !== 4) { + throw new RuntimeException(sprintf( + 'A palette row must supply R, G, B and Alpha components, %s given!', + var_export($row, true) + )); + } + list($R, $G, $B, $Alpha) = $row; + $ID = count($this->Palette); + $this->Palette[$ID] = [ + "R" => trim($R), + "G" => trim($G), + "B" => trim($B), + "Alpha" => trim($Alpha) + ]; + } + fclose($fileHandle); + + /* Apply changes to current series */ + $ID = 0; + if (isset($this->Data["Series"])) { + foreach ($this->Data["Series"] as $Key => $Value) { + if (!isset($this->Palette[$ID])) { + $this->Data["Series"][$Key]["Color"] = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 0]; + } else { + $this->Data["Series"][$Key]["Color"] = $this->Palette[$ID]; + } + $ID++; + } + } + } + + /** + * Initialise a given scatter serie + * @param int $ID + * @return null + */ + public function initScatterSerie($ID) + { + if (isset($this->Data["ScatterSeries"][$ID])) { + return null; + } + + $this->Data["ScatterSeries"][$ID]["Description"] = "Scatter " . $ID; + $this->Data["ScatterSeries"][$ID]["isDrawable"] = true; + $this->Data["ScatterSeries"][$ID]["Picture"] = null; + $this->Data["ScatterSeries"][$ID]["Ticks"] = 0; + $this->Data["ScatterSeries"][$ID]["Weight"] = 0; + + if (isset($this->Palette[$ID])) { + $this->Data["ScatterSeries"][$ID]["Color"] = $this->Palette[$ID]; + } else { + $this->Data["ScatterSeries"][$ID]["Color"]["R"] = rand(0, 255); + $this->Data["ScatterSeries"][$ID]["Color"]["G"] = rand(0, 255); + $this->Data["ScatterSeries"][$ID]["Color"]["B"] = rand(0, 255); + $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = 100; + } + } + + /** + * Initialise a given serie + * @param string $Serie + */ + public function initialise($Serie) + { + $ID = 0; + if (isset($this->Data["Series"])) { + $ID = count($this->Data["Series"]); + } + + $this->Data["Series"][$Serie]["Description"] = $Serie; + $this->Data["Series"][$Serie]["isDrawable"] = true; + $this->Data["Series"][$Serie]["Picture"] = null; + $this->Data["Series"][$Serie]["Max"] = null; + $this->Data["Series"][$Serie]["Min"] = null; + $this->Data["Series"][$Serie]["Axis"] = 0; + $this->Data["Series"][$Serie]["Ticks"] = 0; + $this->Data["Series"][$Serie]["Weight"] = 0; + $this->Data["Series"][$Serie]["Shape"] = SERIE_SHAPE_FILLEDCIRCLE; + + if (isset($this->Palette[$ID])) { + $this->Data["Series"][$Serie]["Color"] = $this->Palette[$ID]; + } else { + $this->Data["Series"][$Serie]["Color"]["R"] = rand(0, 255); + $this->Data["Series"][$Serie]["Color"]["G"] = rand(0, 255); + $this->Data["Series"][$Serie]["Color"]["B"] = rand(0, 255); + $this->Data["Series"][$Serie]["Color"]["Alpha"] = 100; + } + $this->Data["Series"][$Serie]["Data"] = []; + } + + /** + * @param int $NormalizationFactor + * @param mixed $UnitChange + * @param int $Round + */ + public function normalize($NormalizationFactor = 100, $UnitChange = null, $Round = 1) + { + $Abscissa = $this->Data["Abscissa"]; + + $SelectedSeries = []; + $MaxVal = 0; + foreach (array_keys($this->Data["Axis"]) as $AxisID) { + if ($UnitChange != null) { + $this->Data["Axis"][$AxisID]["Unit"] = $UnitChange; + } + + foreach ($this->Data["Series"] as $SerieName => $Serie) { + if ($Serie["Axis"] == $AxisID + && $Serie["isDrawable"] == true + && $SerieName != $Abscissa + ) { + $SelectedSeries[$SerieName] = $SerieName; + + if (count($Serie["Data"]) > $MaxVal) { + $MaxVal = count($Serie["Data"]); + } + } + } + } + + for ($i = 0; $i <= $MaxVal - 1; $i++) { + $Factor = 0; + foreach ($SelectedSeries as $Key => $SerieName) { + $Value = $this->Data["Series"][$SerieName]["Data"][$i]; + if ($Value != VOID) { + $Factor = $Factor + abs($Value); + } + } + + if ($Factor != 0) { + $Factor = $NormalizationFactor / $Factor; + + foreach ($SelectedSeries as $Key => $SerieName) { + $Value = $this->Data["Series"][$SerieName]["Data"][$i]; + + if ($Value != VOID && $Factor != $NormalizationFactor) { + $this->Data["Series"][$SerieName]["Data"][$i] = round(abs($Value) * $Factor, $Round); + } elseif ($Value == VOID || $Value == 0) { + $this->Data["Series"][$SerieName]["Data"][$i] = VOID; + } elseif ($Factor == $NormalizationFactor) { + $this->Data["Series"][$SerieName]["Data"][$i] = $NormalizationFactor; + } + } + } + } + + foreach ($SelectedSeries as $Key => $SerieName) { + $this->Data["Series"][$SerieName]["Max"] = max( + $this->stripVOID($this->Data["Series"][$SerieName]["Data"]) + ); + $this->Data["Series"][$SerieName]["Min"] = min( + $this->stripVOID($this->Data["Series"][$SerieName]["Data"]) + ); + } + } + + /** + * Load data from a CSV (or similar) data source + * @param string $FileName + * @param array $Options + */ + public function importFromCSV($FileName, array $Options = []) + { + $Delimiter = isset($Options["Delimiter"]) ? $Options["Delimiter"] : ","; + $GotHeader = isset($Options["GotHeader"]) ? $Options["GotHeader"] : false; + $SkipColumns = isset($Options["SkipColumns"]) ? $Options["SkipColumns"] : [-1]; + $DefaultSerieName = isset($Options["DefaultSerieName"]) ? $Options["DefaultSerieName"] : "Serie"; + + $Handle = @fopen($FileName, "r"); + if ($Handle) { + $HeaderParsed = false; + $SerieNames = []; + while (!feof($Handle)) { + $Buffer = fgets($Handle, 4096); + $Buffer = str_replace(chr(10), "", $Buffer); + $Buffer = str_replace(chr(13), "", $Buffer); + $Values = preg_split("/" . $Delimiter . "/", $Buffer); + + if ($Buffer != "") { + if ($GotHeader && !$HeaderParsed) { + foreach ($Values as $Key => $Name) { + if (!in_array($Key, $SkipColumns)) { + $SerieNames[$Key] = $Name; + } + } + $HeaderParsed = true; + } else { + if (!count($SerieNames)) { + foreach ($Values as $Key => $Name) { + if (!in_array($Key, $SkipColumns)) { + $SerieNames[$Key] = $DefaultSerieName . $Key; + } + } + } + foreach ($Values as $Key => $Value) { + if (!in_array($Key, $SkipColumns)) { + $this->addPoints($Value, $SerieNames[$Key]); + } + } + } + } + } + fclose($Handle); + } + } + + /** + * Create a dataset based on a formula + * + * @param string $SerieName + * @param string $Formula + * @param array $Options + * @return null + */ + public function createFunctionSerie($SerieName, $Formula = "", array $Options = []) + { + $MinX = isset($Options["MinX"]) ? $Options["MinX"] : -10; + $MaxX = isset($Options["MaxX"]) ? $Options["MaxX"] : 10; + $XStep = isset($Options["XStep"]) ? $Options["XStep"] : 1; + $AutoDescription = isset($Options["AutoDescription"]) ? $Options["AutoDescription"] : false; + $RecordAbscissa = isset($Options["RecordAbscissa"]) ? $Options["RecordAbscissa"] : false; + $AbscissaSerie = isset($Options["AbscissaSerie"]) ? $Options["AbscissaSerie"] : "Abscissa"; + + if ($Formula == "") { + return null; + } + + $Result = []; + $Abscissa = []; + for ($i = $MinX; $i <= $MaxX; $i = $i + $XStep) { + $Expression = "\$return = '!'.(" . str_replace("z", $i, $Formula) . ");"; + if (@eval($Expression) === false) { + $return = VOID; + } + if ($return == "!") { + $return = VOID; + } else { + $return = $this->right($return, strlen($return) - 1); + } + if ($return == "NAN") { + $return = VOID; + } + if ($return == "INF") { + $return = VOID; + } + if ($return == "-INF") { + $return = VOID; + } + + $Abscissa[] = $i; + $Result[] = $return; + } + + $this->addPoints($Result, $SerieName); + if ($AutoDescription) { + $this->setSerieDescription($SerieName, $Formula); + } + if ($RecordAbscissa) { + $this->addPoints($Abscissa, $AbscissaSerie); + } + } + + /** + * @param mixed $Series + */ + public function negateValues($Series) + { + if (!is_array($Series)) { + $Series = $this->convertToArray($Series); + } + foreach ($Series as $Key => $SerieName) { + if (isset($this->Data["Series"][$SerieName])) { + $Data = []; + foreach ($this->Data["Series"][$SerieName]["Data"] as $Key => $Value) { + if ($Value == VOID) { + $Data[] = VOID; + } else { + $Data[] = -$Value; + } + } + $this->Data["Series"][$SerieName]["Data"] = $Data; + $this->Data["Series"][$SerieName]["Max"] = max( + $this->stripVOID($this->Data["Series"][$SerieName]["Data"]) + ); + $this->Data["Series"][$SerieName]["Min"] = min( + $this->stripVOID($this->Data["Series"][$SerieName]["Data"]) + ); + } + } + } + + /** + * Return the data & configuration of the series + * @return array + */ + public function getData() + { + return $this->Data; + } + + /** + * Save a palette element + * + * @param integer $ID + * @param string $Color + */ + public function savePalette($ID, $Color) + { + $this->Palette[$ID] = $Color; + } + + /** + * Return the palette of the series + * @return array + */ + public function getPalette() + { + return $this->Palette; + } + + /** + * Called by the scaling algorithm to save the config + * @param mixed $Axis + */ + public function saveAxisConfig($Axis) + { + $this->Data["Axis"] = $Axis; + } + + /** + * Save the Y Margin if set + * @param mixed $Value + */ + public function saveYMargin($Value) + { + $this->Data["YMargin"] = $Value; + } + + /** + * Save extended configuration to the pData object + * @param string $Tag + * @param mixed $Values + */ + public function saveExtendedData($Tag, $Values) + { + $this->Data["Extended"][$Tag] = $Values; + } + + /** + * Called by the scaling algorithm to save the orientation of the scale + * @param mixed $Orientation + */ + public function saveOrientation($Orientation) + { + $this->Data["Orientation"] = $Orientation; + } + + /** + * Convert a string to a single elements array + * @param mixed $Value + * @return array + */ + public function convertToArray($Value) + { + return [$Value]; + } + + /** + * Class string wrapper + * @return string + */ + public function __toString() + { + return "pData object."; + } + + /** + * @param string $value + * @param int $NbChar + * @return string + */ + public function left($value, $NbChar) + { + return substr($value, 0, $NbChar); + } + + /** + * @param string $value + * @param int $NbChar + * @return string + */ + public function right($value, $NbChar) + { + return substr($value, strlen($value) - $NbChar, $NbChar); + } + + /** + * @param string $value + * @param int $Depart + * @param int $NbChar + * @return string + */ + public function mid($value, $Depart, $NbChar) + { + return substr($value, $Depart - 1, $NbChar); + } +} diff --git a/vendor/szymach/c-pchart/src/Draw.php b/vendor/szymach/c-pchart/src/Draw.php new file mode 100644 index 0000000..cfbf68e --- /dev/null +++ b/vendor/szymach/c-pchart/src/Draw.php @@ -0,0 +1,10363 @@ +Shadow; + if (!$NoFill) { + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) { + $Shadow[] = $Points[$i] + $this->ShadowX; + $Shadow[] = $Points[$i + 1] + $this->ShadowY; + } + $this->drawPolygon( + $Shadow, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa, + "NoBorder" => true + ] + ); + } + + $FillColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + + if (count($Points) >= 6) { + $this->imageFilledPolygonWrapper($this->Picture, $Points, count($Points) / 2, $FillColor); + } + } + + if (!$NoBorder) { + $Points = $Backup; + + if ($NoFill) { + $BorderSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + } else { + $BorderSettings = [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha + ]; + } + + for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) { + if (isset($Points[$i + 2]) + && !($Points[$i] == $Points[$i + 2] && $Points[$i] == $SkipX) + && !($Points[$i + 1] == $Points[$i + 3] && $Points[$i + 1] == $SkipY) + ) { + $this->drawLine( + $Points[$i], + $Points[$i + 1], + $Points[$i + 2], + $Points[$i + 3], + $BorderSettings + ); + } elseif (!($Points[$i] == $Points[0] && $Points[$i] == $SkipX) + && !($Points[$i + 1] == $Points[1] && $Points[$i + 1] == $SkipY) + ) { + $this->drawLine($Points[$i], $Points[$i + 1], $Points[0], $Points[1], $BorderSettings); + } + } + } + + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a rectangle with rounded corners + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param int|float $Radius + * @param array $Format + * @return null|integer + */ + public function drawRoundedRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2); + + if ($X2 - $X1 < $Radius) { + $Radius = floor((($X2 - $X1)) / 2); + } + if ($Y2 - $Y1 < $Radius) { + $Radius = floor((($Y2 - $Y1)) / 2); + } + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => true]; + + if ($Radius <= 0) { + $this->drawRectangle($X1, $Y1, $X2, $Y2, $Color); + return 0; + } + + if ($this->Antialias) { + $this->drawLine($X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color); + $this->drawLine($X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color); + $this->drawLine($X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color); + $this->drawLine($X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color); + } else { + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + imageline($this->Picture, $X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color); + imageline($this->Picture, $X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color); + imageline($this->Picture, $X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color); + imageline($this->Picture, $X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color); + } + + $Step = 360 / (2 * PI * $Radius); + for ($i = 0; $i <= 90; $i = $i + $Step) { + $X = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius; + $Y = sin(($i + 180) * PI / 180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + + $X = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius; + $Y = sin(($i + 90) * PI / 180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + + $X = cos($i * PI / 180) * $Radius + $X2 - $Radius; + $Y = sin($i * PI / 180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + + $X = cos(($i + 270) * PI / 180) * $Radius + $X2 - $Radius; + $Y = sin(($i + 270) * PI / 180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + } + } + + /** + * Draw a rectangle with rounded corners + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param int|float $Radius + * @param array $Format + * @return null|integer + */ + public function drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + + /* Temporary fix for AA issue */ + $Y1 = (int) floor($Y1); + $Y2 = (int) floor($Y2); + $X1 = (int) floor($X1); + $X2 = (int) floor($X2); + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + if ($BorderR == -1) { + $BorderR = $R; + $BorderG = $G; + $BorderB = $B; + } + + list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2); + + if ($X2 - $X1 < $Radius * 2) { + $Radius = (int) floor((($X2 - $X1)) / 4); + } + if ($Y2 - $Y1 < $Radius * 2) { + $Radius = (int) floor((($Y2 - $Y1)) / 4); + } + + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + $this->drawRoundedFilledRectangle( + $X1 + $this->ShadowX, + $Y1 + $this->ShadowY, + $X2 + $this->ShadowX, + $Y2 + $this->ShadowY, + $Radius, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa + ] + ); + } + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => true]; + + if ($Radius <= 0) { + $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color); + return 0; + } + + $YTop = $Y1 + $Radius; + $YBottom = $Y2 - $Radius; + + $Step = 360 / (2 * PI * $Radius); + $Positions = []; + $Radius--; + $MinY = null; + $MaxY = null; + for ($i = 0; $i <= 90; $i = $i + $Step) { + $Xp1 = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius; + $Xp2 = cos(((90 - $i) + 270) * PI / 180) * $Radius + $X2 - $Radius; + $Yp = (int) floor(sin(($i + 180) * PI / 180) * $Radius + $YTop); + if (null === $MinY || $Yp > $MinY) { + $MinY = $Yp; + } + + if ($Xp1 <= floor($X1)) { + $Xp1++; + } + if ($Xp2 >= floor($X2)) { + $Xp2--; + } + $Xp1++; + + if (!isset($Positions[$Yp])) { + $Positions[$Yp]["X1"] = $Xp1; + $Positions[$Yp]["X2"] = $Xp2; + } else { + $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2; + $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2; + } + + $Xp1 = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius; + $Xp2 = cos((90 - $i) * PI / 180) * $Radius + $X2 - $Radius; + $Yp = (int) floor(sin(($i + 90) * PI / 180) * $Radius + $YBottom); + if (null === $MaxY || $Yp < $MaxY) { + $MaxY = $Yp; + } + + if ($Xp1 <= floor($X1)) { + $Xp1++; + } + if ($Xp2 >= floor($X2)) { + $Xp2--; + } + $Xp1++; + + if (!isset($Positions[$Yp])) { + $Positions[$Yp]["X1"] = $Xp1; + $Positions[$Yp]["X2"] = $Xp2; + } else { + $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2; + $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2; + } + } + + $ManualColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + foreach ($Positions as $Yp => $Bounds) { + $X1 = (int) $Bounds["X1"]; + $X1Dec = $this->getFirstDecimal($X1); + if ($X1Dec != 0) { + $X1 = (int) floor($X1) + 1; + } + $X2 = (int) $Bounds["X2"]; + $X2Dec = $this->getFirstDecimal($X2); + if ($X2Dec != 0) { + $X2 = (int) floor($X2) - 1; + } + imageline($this->Picture, $X1, $Yp, $X2, $Yp, $ManualColor); + } + $this->drawFilledRectangle($X1, $MinY + 1, (int) floor($X2), $MaxY - 1, $Color); + + $Radius++; + $this->drawRoundedRectangle( + $X1, + $Y1, + $X2 + 1, + $Y2 - 1, + $Radius, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a rectangle + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param array $Format + */ + public function drawRectangle($X1, $Y1, $X2, $Y2, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : false; + + if ($X1 > $X2) { + list($X1, $X2) = [$X2, $X1]; + } + if ($Y1 > $Y2) { + list($Y1, $Y2) = [$Y2, $Y1]; + } + + if ($this->Antialias) { + if ($NoAngle) { + $this->drawLine( + $X1 + 1, + $Y1, + $X2 - 1, + $Y1, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X2, + $Y1 + 1, + $X2, + $Y2 - 1, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X2 - 1, + $Y2, + $X1 + 1, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X1, + $Y1 + 1, + $X1, + $Y2 - 1, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + } else { + $this->drawLine( + $X1 + 1, + $Y1, + $X2 - 1, + $Y1, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X2, + $Y1, + $X2, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X2 - 1, + $Y2, + $X1 + 1, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + $this->drawLine( + $X1, + $Y1, + $X1, + $Y2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + } + } else { + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + imagerectangle($this->Picture, $X1, $Y1, $X2, $Y2, $Color); + } + } + + /** + * Draw a filled rectangle + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param array $Format + */ + public function drawFilledRectangle($X1, $Y1, $X2, $Y2, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : null; + $Dash = isset($Format["Dash"]) ? $Format["Dash"] : false; + $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4; + $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0; + $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0; + $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0; + $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : false; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + if ($X1 > $X2) { + list($X1, $X2) = [$X2, $X1]; + } + if ($Y1 > $Y2) { + list($Y1, $Y2) = [$Y2, $Y1]; + } + + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + $this->drawFilledRectangle( + $X1 + $this->ShadowX, + $Y1 + $this->ShadowY, + $X2 + $this->ShadowX, + $Y2 + $this->ShadowY, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa, + "Ticks" => $Ticks, + "NoAngle" => $NoAngle + ] + ); + } + + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + if ($NoAngle) { + imagefilledrectangle($this->Picture, ceil($X1) + 1, ceil($Y1), floor($X2) - 1, floor($Y2), $Color); + imageline($this->Picture, ceil($X1), ceil($Y1) + 1, ceil($X1), floor($Y2) - 1, $Color); + imageline($this->Picture, floor($X2), ceil($Y1) + 1, floor($X2), floor($Y2) - 1, $Color); + } else { + imagefilledrectangle($this->Picture, ceil($X1), ceil($Y1), floor($X2), floor($Y2), $Color); + } + if ($Dash) { + if ($BorderR != -1) { + $iX1 = $X1 + 1; + $iY1 = $Y1 + 1; + $iX2 = $X2 - 1; + $iY2 = $Y2 - 1; + } else { + $iX1 = $X1; + $iY1 = $Y1; + $iX2 = $X2; + $iY2 = $Y2; + } + + $Color = $this->allocateColor($this->Picture, $DashR, $DashG, $DashB, $Alpha); + $Y = $iY1 - $DashStep; + for ($X = $iX1; $X <= $iX2 + ($iY2 - $iY1); $X = $X + $DashStep) { + $Y = $Y + $DashStep; + if ($X > $iX2) { + $Xa = $X - ($X - $iX2); + $Ya = $iY1 + ($X - $iX2); + } else { + $Xa = $X; + $Ya = $iY1; + } + if ($Y > $iY2) { + $Xb = $iX1 + ($Y - $iY2); + $Yb = $Y - ($Y - $iY2); + } else { + $Xb = $iX1; + $Yb = $Y; + } + imageline($this->Picture, $Xa, $Ya, $Xb, $Yb, $Color); + } + } + + if ($this->Antialias && !$NoBorder) { + if ($X1 < ceil($X1)) { + $AlphaA = $Alpha * (ceil($X1) - $X1); + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA); + imageline($this->Picture, ceil($X1) - 1, ceil($Y1), ceil($X1) - 1, floor($Y2), $Color); + } + + if ($Y1 < ceil($Y1)) { + $AlphaA = $Alpha * (ceil($Y1) - $Y1); + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA); + imageline($this->Picture, ceil($X1), ceil($Y1) - 1, floor($X2), ceil($Y1) - 1, $Color); + } + + if ($X2 > floor($X2)) { + $AlphaA = $Alpha * (.5 - ($X2 - floor($X2))); + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA); + imageline($this->Picture, floor($X2) + 1, ceil($Y1), floor($X2) + 1, floor($Y2), $Color); + } + + if ($Y2 > floor($Y2)) { + $AlphaA = $Alpha * (.5 - ($Y2 - floor($Y2))); + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA); + imageline($this->Picture, ceil($X1), floor($Y2) + 1, floor($X2), floor($Y2) + 1, $Color); + } + } + + if ($BorderR != -1) { + $this->drawRectangle( + $X1, + $Y1, + $X2, + $Y2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $Ticks, + "NoAngle" => $NoAngle + ] + ); + } + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a rectangular marker of the specified size + * @param int $X + * @param int $Y + * @param array $Format + */ + public function drawRectangleMarker($X, $Y, array $Format = []) + { + $Size = isset($Format["Size"]) ? $Format["Size"] : 4; + + $HalfSize = floor($Size / 2); + $this->drawFilledRectangle($X - $HalfSize, $Y - $HalfSize, $X + $HalfSize, $Y + $HalfSize, $Format); + } + + /** + * Drawn a spline based on the bezier public function + * @param array $Coordinates + * @param array $Format + * @return array + */ + public function drawSpline(array $Coordinates, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Force = isset($Format["Force"]) ? $Format["Force"] : 30; + $Forces = isset($Format["Forces"]) ? $Format["Forces"] : null; + $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : false; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : false; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + + $Cpt = null; + $Mode = null; + $Result = []; + for ($i = 1; $i <= count($Coordinates) - 1; $i++) { + $X1 = $Coordinates[$i - 1][0]; + $Y1 = $Coordinates[$i - 1][1]; + $X2 = $Coordinates[$i][0]; + $Y2 = $Coordinates[$i][1]; + + if ($Forces != null) { + $Force = $Forces[$i]; + } + + /* First segment */ + if ($i == 1) { + $Xv1 = $X1; + $Yv1 = $Y1; + } else { + $Angle1 = $this->getAngle($XLast, $YLast, $X1, $Y1); + $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2); + $XOff = cos($Angle2 * PI / 180) * $Force + $X1; + $YOff = sin($Angle2 * PI / 180) * $Force + $Y1; + + $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff; + $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff; + } + + /* Last segment */ + if ($i == count($Coordinates) - 1) { + $Xv2 = $X2; + $Yv2 = $Y2; + } else { + $Angle1 = $this->getAngle($X2, $Y2, $Coordinates[$i + 1][0], $Coordinates[$i + 1][1]); + $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2); + $XOff = cos(($Angle2 + 180) * PI / 180) * $Force + $X2; + $YOff = sin(($Angle2 + 180) * PI / 180) * $Force + $Y2; + + $Xv2 = cos(($Angle1 + 180) * PI / 180) * $Force + $XOff; + $Yv2 = sin(($Angle1 + 180) * PI / 180) * $Force + $YOff; + } + + $Path = $this->drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, $Format); + if ($PathOnly) { + $Result[] = $Path; + } + + $XLast = $X1; + $YLast = $Y1; + } + + return $Result; + } + + /** + * Draw a bezier curve with two controls points + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param int $Xv1 + * @param int $Yv1 + * @param int $Xv2 + * @param int $Yv2 + * @param array $Format + * @return array + */ + public function drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : false; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : null; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : false; + $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : false; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : false; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10; + $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : .5; + $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : false; + + if ($Segments == null) { + $Length = $this->getLength($X1, $Y1, $X2, $Y2); + $Precision = ($Length * 125) / 1000; + } else { + $Precision = $Segments; + } + $P[0]["X"] = $X1; + $P[0]["Y"] = $Y1; + $P[1]["X"] = $Xv1; + $P[1]["Y"] = $Yv1; + $P[2]["X"] = $Xv2; + $P[2]["Y"] = $Yv2; + $P[3]["X"] = $X2; + $P[3]["Y"] = $Y2; + + /* Compute the bezier points */ + $Q = []; + $ID = 0; + for ($i = 0; $i <= $Precision; $i = $i + 1) { + $u = $i / $Precision; + + $C = []; + $C[0] = (1 - $u) * (1 - $u) * (1 - $u); + $C[1] = ($u * 3) * (1 - $u) * (1 - $u); + $C[2] = 3 * $u * $u * (1 - $u); + $C[3] = $u * $u * $u; + + for ($j = 0; $j <= 3; $j++) { + if (!isset($Q[$ID])) { + $Q[$ID] = []; + } + if (!isset($Q[$ID]["X"])) { + $Q[$ID]["X"] = 0; + } + if (!isset($Q[$ID]["Y"])) { + $Q[$ID]["Y"] = 0; + } + + $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j]; + $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j]; + } + $ID++; + } + $Q[$ID]["X"] = $X2; + $Q[$ID]["Y"] = $Y2; + + if (!$NoDraw) { + /* Display the control points */ + if ($ShowC && !$PathOnly) { + $Xv1 = floor($Xv1); + $Yv1 = floor($Yv1); + $Xv2 = floor($Xv2); + $Yv2 = floor($Yv2); + + $this->drawLine($X1, $Y1, $X2, $Y2, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]); + + $MyMarkerSettings = [ + "R" => 255, + "G" => 0, + "B" => 0, + "BorderR" => 255, + "BorderB" => 255, + "BorderG" => 255, + "Size" => 4 + ]; + $this->drawRectangleMarker($Xv1, $Yv1, $MyMarkerSettings); + $this->drawText($Xv1 + 4, $Yv1, "v1"); + $MyMarkerSettings = [ + "R" => 0, + "G" => 0, + "B" => 255, + "BorderR" => 255, + "BorderB" => 255, + "BorderG" => 255, + "Size" => 4 + ]; + $this->drawRectangleMarker($Xv2, $Yv2, $MyMarkerSettings); + $this->drawText($Xv2 + 4, $Yv2, "v2"); + } + + /* Draw the bezier */ + $LastX = null; + $LastY = null; + $Cpt = null; + $Mode = null; + $ArrowS = []; + foreach ($Q as $Point) { + $X = $Point["X"]; + $Y = $Point["Y"]; + + /* Get the first segment */ + if (!count($ArrowS) && $LastX != null && $LastY != null) { + $ArrowS["X2"] = $LastX; + $ArrowS["Y2"] = $LastY; + $ArrowS["X1"] = $X; + $ArrowS["Y1"] = $Y; + } + + if ($LastX != null && $LastY != null && !$PathOnly) { + list($Cpt, $Mode) = $this->drawLine( + $LastX, + $LastY, + $X, + $Y, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Cpt" => $Cpt, + "Mode" => $Mode, + "Weight" => $Weight + ] + ); + } + /* Get the last segment */ + $ArrowE["X1"] = $LastX; + $ArrowE["Y1"] = $LastY; + $ArrowE["X2"] = $X; + $ArrowE["Y2"] = $Y; + + $LastX = $X; + $LastY = $Y; + } + + if ($DrawArrow && !$PathOnly) { + $ArrowSettings = [ + "FillR" => $R, + "FillG" => $G, + "FillB" => $B, + "Alpha" => $Alpha, + "Size" => $ArrowSize, + "Ratio" => $ArrowRatio + ]; + if ($ArrowTwoHeads) { + $this->drawArrow($ArrowS["X1"], $ArrowS["Y1"], $ArrowS["X2"], $ArrowS["Y2"], $ArrowSettings); + } + $this->drawArrow($ArrowE["X1"], $ArrowE["Y1"], $ArrowE["X2"], $ArrowE["Y2"], $ArrowSettings); + } + } + return $Q; + } + + /** + * Draw a line between two points + * @param int|float $X1 + * @param int|float $Y1 + * @param int|float $X2 + * @param int|float $Y2 + * @param array $Format + * @return array|int + */ + public function drawLine($X1, $Y1, $X2, $Y2, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null; + + if ($this->Antialias == false && $Ticks == null) { + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $ShadowColor = $this->allocateColor( + $this->Picture, + $this->ShadowR, + $this->ShadowG, + $this->ShadowB, + $this->Shadowa + ); + imageline( + $this->Picture, + intval($X1 + $this->ShadowX), + intval($Y1 + $this->ShadowY), + intval($X2 + $this->ShadowX), + intval($Y2 + $this->ShadowY), + $ShadowColor + ); + } + + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + imageline($this->Picture, (int) $X1, (int) $Y1, (int) $X2, (int) $Y2, $Color); + return 0; + } + + $Distance = sqrt(($X2 - $X1) * ($X2 - $X1) + ($Y2 - $Y1) * ($Y2 - $Y1)); + if ($Distance == 0) { + return -1; + } + + /* Derivative algorithm for overweighted lines, re-route to polygons primitives */ + if ($Weight != null) { + $Angle = $this->getAngle($X1, $Y1, $X2, $Y2); + $PolySettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderAlpha" => $Alpha]; + + if ($Ticks == null) { + $Points = []; + $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X1; + $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y1; + $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X1; + $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y1; + $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X2; + $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y2; + $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X2; + $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y2; + + $this->drawPolygon($Points, $PolySettings); + } else { + for ($i = 0; $i <= $Distance; $i = $i + $Ticks * 2) { + $Xa = (($X2 - $X1) / $Distance) * $i + $X1; + $Ya = (($Y2 - $Y1) / $Distance) * $i + $Y1; + $Xb = (($X2 - $X1) / $Distance) * ($i + $Ticks) + $X1; + $Yb = (($Y2 - $Y1) / $Distance) * ($i + $Ticks) + $Y1; + + $Points = []; + $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xa; + $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Ya; + $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xa; + $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Ya; + $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xb; + $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Yb; + $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xb; + $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Yb; + + $this->drawPolygon($Points, $PolySettings); + } + } + + return 1; + } + + $XStep = ($X2 - $X1) / $Distance; + $YStep = ($Y2 - $Y1) / $Distance; + + for ($i = 0; $i <= $Distance; $i++) { + $X = $i * $XStep + $X1; + $Y = $i * $YStep + $Y1; + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + + if ($Threshold != null) { + foreach ($Threshold as $Key => $Parameters) { + if ($Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) { + if (isset($Parameters["R"])) { + $RT = $Parameters["R"]; + } else { + $RT = 0; + } + if (isset($Parameters["G"])) { + $GT = $Parameters["G"]; + } else { + $GT = 0; + } + if (isset($Parameters["B"])) { + $BT = $Parameters["B"]; + } else { + $BT = 0; + } + if (isset($Parameters["Alpha"])) { + $AlphaT = $Parameters["Alpha"]; + } else { + $AlphaT = 0; + } + $Color = ["R" => $RT, "G" => $GT, "B" => $BT, "Alpha" => $AlphaT]; + } + } + } + + if ($Ticks != null) { + if ($Cpt % $Ticks == 0) { + $Cpt = 0; + if ($Mode == 1) { + $Mode = 0; + } else { + $Mode = 1; + } + } + + if ($Mode == 1) { + $this->drawAntialiasPixel($X, $Y, $Color); + } + $Cpt++; + } else { + $this->drawAntialiasPixel($X, $Y, $Color); + } + } + + return [$Cpt, $Mode]; + } + + /** + * Draw a circle + * @param int $Xc + * @param int $Yc + * @param int|float $Height + * @param int|float $Width + * @param array $Format + */ + public function drawCircle($Xc, $Yc, $Height, $Width, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + + $Height = abs($Height); + $Width = abs($Width); + + if ($Height == 0) { + $Height = 1; + } + if ($Width == 0) { + $Width = 1; + } + $Xc = floor($Xc); + $Yc = floor($Yc); + + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + $this->drawCircle( + $Xc + $this->ShadowX, + $Yc + $this->ShadowY, + $Height, + $Width, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa, + "Ticks" => $Ticks + ] + ); + } + + if ($Width == 0) { + $Width = $Height; + } + if ($R < 0) { + $R = 0; + } if ($R > 255) { + $R = 255; + } + if ($G < 0) { + $G = 0; + } if ($G > 255) { + $G = 255; + } + if ($B < 0) { + $B = 0; + } if ($B > 255) { + $B = 255; + } + + $Step = 360 / (2 * PI * max($Width, $Height)); + $Mode = 1; + $Cpt = 1; + for ($i = 0; $i <= 360; $i = $i + $Step) { + $X = cos($i * PI / 180) * $Height + $Xc; + $Y = sin($i * PI / 180) * $Width + $Yc; + + if ($Ticks != null) { + if ($Cpt % $Ticks == 0) { + $Cpt = 0; + if ($Mode == 1) { + $Mode = 0; + } else { + $Mode = 1; + } + } + + if ($Mode == 1) { + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + } + $Cpt++; + } else { + $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + } + } + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a filled circle + * @param int $X + * @param int $Y + * @param int|float $Radius + * @param array $Format + */ + public function drawFilledCircle($X, $Y, $Radius, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + + if ($Radius == 0) { + $Radius = 1; + } + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + $X = (int) floor($X); + $Y = (int) floor($Y); + + $Radius = abs($Radius); + + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + $this->drawFilledCircle( + $X + $this->ShadowX, + $Y + $this->ShadowY, + $Radius, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa, + "Ticks" => $Ticks + ] + ); + } + + $this->Mask = []; + $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + for ($i = 0; $i <= $Radius * 2; $i++) { + $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i)); + $XPos = (int) floor($Slice); + $YPos = (int) ($Y + $i - $Radius); + + $this->Mask[$X - $XPos][$YPos] = true; + $this->Mask[$X + $XPos][$YPos] = true; + imageline($this->Picture, $X - $XPos, $YPos, $X + $XPos, $YPos, $Color); + } + if ($this->Antialias) { + $this->drawCircle( + $X, + $Y, + $Radius, + $Radius, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + } + $this->Mask = []; + + if ($BorderR != -1) { + $this->drawCircle( + $X, + $Y, + $Radius, + $Radius, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $Ticks + ] + ); + } + $this->Shadow = $RestoreShadow; + } + + /** + * Write text + * @param int|float $X + * @param int|float $Y + * @param string $Text + * @param array $Format + * @return array + */ + public function drawText($X, $Y, $Text, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA; + $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : false; + $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : false; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : false; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; + $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : false; + + $Shadow = $this->Shadow; + if ($NoShadow) { + $this->Shadow = false; + } + + if ($BoxSurrounding != "") { + $BoxBorderR = $BoxR - $BoxSurrounding; + $BoxBorderG = $BoxG - $BoxSurrounding; + $BoxBorderB = $BoxB - $BoxSurrounding; + $BoxBorderAlpha = $BoxAlpha; + } + + if ($ShowOrigine) { + $MyMarkerSettings = [ + "R" => 255, + "G" => 0, + "B" => 0, + "BorderR" => 255, + "BorderB" => 255, + "BorderG" => 255, + "Size" => 4 + ]; + $this->drawRectangleMarker($X, $Y, $MyMarkerSettings); + } + + $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, $Angle, $Text); + + if ($DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270)) { + $T[0]["X"] = 0; + $T[0]["Y"] = 0; + $T[1]["X"] = 0; + $T[1]["Y"] = 0; + $T[2]["X"] = 0; + $T[2]["Y"] = 0; + $T[3]["X"] = 0; + $T[3]["Y"] = 0; + if ($Angle == 0) { + $T[0]["X"] = -$TOffset; + $T[0]["Y"] = $TOffset; + $T[1]["X"] = $TOffset; + $T[1]["Y"] = $TOffset; + $T[2]["X"] = $TOffset; + $T[2]["Y"] = -$TOffset; + $T[3]["X"] = -$TOffset; + $T[3]["Y"] = -$TOffset; + } + + $X1 = min($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) - $BorderOffset + 3; + $Y1 = min($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) - $BorderOffset; + $X2 = max($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) + $BorderOffset + 3; + $Y2 = max($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) + $BorderOffset - 3; + + $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; + $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; + $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; + $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; + + $Settings = [ + "R" => $BoxR, + "G" => $BoxG, + "B" => $BoxB, + "Alpha" => $BoxAlpha, + "BorderR" => $BoxBorderR, + "BorderG" => $BoxBorderG, + "BorderB" => $BoxBorderB, + "BorderAlpha" => $BoxBorderAlpha + ]; + + if ($BoxRounded) { + $this->drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $RoundedRadius, $Settings); + } else { + $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Settings); + } + } + + $X = (int) ($X - $TxtPos[$Align]["X"] + $X); + $Y = (int) ($Y - $TxtPos[$Align]["Y"] + $Y); + + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $C_ShadowColor = $this->allocateColor( + $this->Picture, + $this->ShadowR, + $this->ShadowG, + $this->ShadowB, + $this->Shadowa + ); + imagettftext( + $this->Picture, + $FontSize, + $Angle, + (int) ($X + $this->ShadowX), + (int) ($Y + $this->ShadowY), + (int) $C_ShadowColor, + $FontName, + $Text + ); + } + + $C_TextColor = $this->AllocateColor($this->Picture, $R, $G, $B, $Alpha); + imagettftext($this->Picture, $FontSize, $Angle, $X, $Y, $C_TextColor, $FontName, $Text); + + $this->Shadow = $Shadow; + + return $TxtPos; + } + + /** + * Draw a gradient within a defined area + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param int $Direction + * @param array $Format + * @return null|integer + */ + public function drawGradientArea($X1, $Y1, $X2, $Y2, $Direction, array $Format = []) + { + $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90; + $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90; + $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90; + $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0; + $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0; + $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Levels = isset($Format["Levels"]) ? $Format["Levels"] : null; + + $Shadow = $this->Shadow; + $this->Shadow = false; + + if ($StartR == $EndR && $StartG == $EndG && $StartB == $EndB) { + $this->drawFilledRectangle( + $X1, + $Y1, + $X2, + $Y2, + ["R" => $StartR, "G" => $StartG, "B" => $StartB, "Alpha" => $Alpha] + ); + return 0; + } + + if ($Levels != null) { + $EndR = $StartR + $Levels; + $EndG = $StartG + $Levels; + $EndB = $StartB + $Levels; + } + + if ($X1 > $X2) { + list($X1, $X2) = [$X2, $X1]; + } + if ($Y1 > $Y2) { + list($Y1, $Y2) = [$Y2, $Y1]; + } + + if ($Direction == DIRECTION_VERTICAL) { + $Width = abs($Y2 - $Y1); + } + if ($Direction == DIRECTION_HORIZONTAL) { + $Width = abs($X2 - $X1); + } + + $Step = max(abs($EndR - $StartR), abs($EndG - $StartG), abs($EndB - $StartB)); + $StepSize = $Width / $Step; + $RStep = ($EndR - $StartR) / $Step; + $GStep = ($EndG - $StartG) / $Step; + $BStep = ($EndB - $StartB) / $Step; + + $R = $StartR; + $G = $StartG; + $B = $StartB; + switch ($Direction) { + case DIRECTION_VERTICAL: + $StartY = $Y1; + $EndY = floor($Y2) + 1; + $LastY2 = $StartY; + for ($i = 0; $i <= $Step; $i++) { + $Y2 = floor($StartY + ($i * $StepSize)); + + if ($Y2 > $EndY) { + $Y2 = $EndY; + } + if (($Y1 != $Y2 && $Y1 < $Y2) || $Y2 == $EndY) { + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color); + $LastY2 = max($LastY2, $Y2); + $Y1 = $Y2 + 1; + } + $R = $R + $RStep; + $G = $G + $GStep; + $B = $B + $BStep; + } + if ($LastY2 < $EndY && isset($Color)) { + for ($i = $LastY2 + 1; $i <= $EndY; $i++) { + $this->drawLine($X1, $i, $X2, $i, $Color); + } + } + break; + + case DIRECTION_HORIZONTAL: + $StartX = $X1; + $EndX = $X2; + for ($i = 0; $i <= $Step; $i++) { + $X2 = floor($StartX + ($i * $StepSize)); + + if ($X2 > $EndX) { + $X2 = $EndX; + } + if (($X1 != $X2 && $X1 < $X2) || $X2 == $EndX) { + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color); + $X1 = $X2 + 1; + } + $R = $R + $RStep; + $G = $G + $GStep; + $B = $B + $BStep; + } + if ($X2 < $EndX && isset($Color)) { + $this->drawFilledRectangle($X2, $Y1, $EndX, $Y2, $Color); + } + break; + } + + $this->Shadow = $Shadow; + } + + /** + * Draw an aliased pixel + * @param int $X + * @param int $Y + * @param array $Format + * @return int|null + */ + public function drawAntialiasPixel($X, $Y, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) { + return -1; + } + + if ($R < 0) { + $R = 0; + } + if ($R > 255) { + $R = 255; + } + if ($G < 0) { + $G = 0; + } + if ($G > 255) { + $G = 255; + } + if ($B < 0) { + $B = 0; + } + if ($B > 255) { + $B = 255; + } + + if (!$this->Antialias) { + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $ShadowColor = $this->allocateColor( + $this->Picture, + $this->ShadowR, + $this->ShadowG, + $this->ShadowB, + $this->Shadowa + ); + imagesetpixel($this->Picture, intval($X + $this->ShadowX), intval($Y + $this->ShadowY), $ShadowColor); + } + + $PlotColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + imagesetpixel($this->Picture, (int) $X, (int) $Y, $PlotColor); + + return 0; + } + + $Xi = floor($X); + $Yi = floor($Y); + + if ($Xi == $X && $Yi == $Y) { + if ($Alpha == 100) { + $this->drawAlphaPixel($X, $Y, 100, $R, $G, $B); + } else { + $this->drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B); + } + } else { + $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; + if ($Alpha1 > $this->AntialiasQuality) { + $this->drawAlphaPixel($Xi, $Yi, $Alpha1, $R, $G, $B); + } + + $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; + if ($Alpha2 > $this->AntialiasQuality) { + $this->drawAlphaPixel($Xi + 1, $Yi, $Alpha2, $R, $G, $B); + } + + $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha; + if ($Alpha3 > $this->AntialiasQuality) { + $this->drawAlphaPixel($Xi, $Yi + 1, $Alpha3, $R, $G, $B); + } + + $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha; + if ($Alpha4 > $this->AntialiasQuality) { + $this->drawAlphaPixel($Xi + 1, $Yi + 1, $Alpha4, $R, $G, $B); + } + } + } + + /** + * Draw a semi-transparent pixel + * @param int $X + * @param int $Y + * @param int $Alpha + * @param int $R + * @param int $G + * @param int $B + * @return null|integer + */ + public function drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B) + { + if (isset($this->Mask[$X]) && isset($this->Mask[$X][$Y])) { + return 0; + } + + if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) { + return -1; + } + if ($R < 0) { + $R = 0; + } if ($R > 255) { + $R = 255; + } + if ($G < 0) { + $G = 0; + } if ($G > 255) { + $G = 255; + } + if ($B < 0) { + $B = 0; + } if ($B > 255) { + $B = 255; + } + + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $AlphaFactor = floor(($Alpha / 100) * $this->Shadowa); + $ShadowColor = $this->allocateColor( + $this->Picture, + $this->ShadowR, + $this->ShadowG, + $this->ShadowB, + $AlphaFactor + ); + imagesetpixel( + $this->Picture, + (int) ($X + $this->ShadowX), + (int) ($Y + $this->ShadowY), + $ShadowColor + ); + } + + $C_Aliased = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + imagesetpixel($this->Picture, (int) $X, (int) $Y, $C_Aliased); + } + + /** + * Load a PNG file and draw it over the chart + * @param int $X + * @param int $Y + * @param string $FileName + */ + public function drawFromPNG($X, $Y, $FileName) + { + $this->drawFromPicture(1, $FileName, $X, $Y); + } + + /** + * Load a GIF file and draw it over the chart + * @param int $X + * @param int $Y + * @param string $FileName + */ + public function drawFromGIF($X, $Y, $FileName) + { + $this->drawFromPicture(2, $FileName, $X, $Y); + } + + /** + * Load a JPEG file and draw it over the chart + * @param int $X + * @param int $Y + * @param string $FileName + */ + public function drawFromJPG($X, $Y, $FileName) + { + $this->drawFromPicture(3, $FileName, $X, $Y); + } + + /** + * Generic loader public function for external pictures + * @param int $PicType + * @param string $FileName + * @param int $X + * @param int $Y + * @return null|integer + */ + public function drawFromPicture($PicType, $FileName, $X, $Y) + { + $X = (int) $X; + $Y = (int) $Y; + + if (file_exists($FileName)) { + list($Width, $Height) = $this->getPicInfo($FileName); + + if ($PicType == 1) { + $Raster = imagecreatefrompng($FileName); + } elseif ($PicType == 2) { + $Raster = imagecreatefromgif($FileName); + } elseif ($PicType == 3) { + $Raster = imagecreatefromjpeg($FileName); + } else { + return 0; + } + + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + if ($PicType == 3) { + $this->drawFilledRectangle( + $X + $this->ShadowX, + $Y + $this->ShadowY, + $X + $Width + $this->ShadowX, + $Y + $Height + $this->ShadowY, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa + ] + ); + } else { + $TranparentID = imagecolortransparent($Raster); + for ($Xc = 0; $Xc <= $Width - 1; $Xc++) { + for ($Yc = 0; $Yc <= $Height - 1; $Yc++) { + $RGBa = imagecolorat($Raster, $Xc, $Yc); + $Values = imagecolorsforindex($Raster, $RGBa); + if ($Values["alpha"] < 120) { + $AlphaFactor = floor( + ($this->Shadowa / 100) * ((100 / 127) * (127 - $Values["alpha"])) + ); + $this->drawAlphaPixel( + $X + $Xc + $this->ShadowX, + $Y + $Yc + $this->ShadowY, + $AlphaFactor, + $this->ShadowR, + $this->ShadowG, + $this->ShadowB + ); + } + } + } + } + } + $this->Shadow = $RestoreShadow; + + imagecopy($this->Picture, $Raster, $X, $Y, 0, 0, $Width, $Height); + imagedestroy($Raster); + } + } + + /** + * Draw an arrow + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @param array $Format + */ + public function drawArrow($X1, $Y1, $X2, $Y2, array $Format = []) + { + $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; + $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; + $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Size = isset($Format["Size"]) ? $Format["Size"] : 10; + $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : .5; + $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : false; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : false; + + /* Calculate the line angle */ + $Angle = $this->getAngle($X1, $Y1, $X2, $Y2); + + /* Override Shadow support, this will be managed internally */ + $RestoreShadow = $this->Shadow; + if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) { + $this->Shadow = false; + $this->drawArrow( + $X1 + $this->ShadowX, + $Y1 + $this->ShadowY, + $X2 + $this->ShadowX, + $Y2 + $this->ShadowY, + [ + "FillR" => $this->ShadowR, + "FillG" => $this->ShadowG, + "FillB" => $this->ShadowB, + "Alpha" => $this->Shadowa, + "Size" => $Size, + "Ratio" => $Ratio, + "TwoHeads" => $TwoHeads, + "Ticks" => $Ticks + ] + ); + } + + /* Draw the 1st Head */ + $TailX = cos(($Angle - 180) * PI / 180) * $Size + $X2; + $TailY = sin(($Angle - 180) * PI / 180) * $Size + $Y2; + + $Points = []; + $Points[] = $X2; + $Points[] = $Y2; + $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX; + $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY; + $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX; + $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY; + $Points[] = $X2; + $Points[] = $Y2; + + /* Visual correction */ + if ($Angle == 180 || $Angle == 360) { + $Points[4] = $Points[2]; + } + if ($Angle == 90 || $Angle == 270) { + $Points[5] = $Points[3]; + } + + $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha); + $this->imageFilledPolygonWrapper($this->Picture, $Points, 4, $ArrowColor); + + $this->drawLine( + $Points[0], + $Points[1], + $Points[2], + $Points[3], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + $this->drawLine( + $Points[2], + $Points[3], + $Points[4], + $Points[5], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + $this->drawLine( + $Points[0], + $Points[1], + $Points[4], + $Points[5], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + + /* Draw the second head */ + if ($TwoHeads) { + $Angle = $this->getAngle($X2, $Y2, $X1, $Y1); + + $TailX2 = cos(($Angle - 180) * PI / 180) * $Size + $X1; + $TailY2 = sin(($Angle - 180) * PI / 180) * $Size + $Y1; + + $Points = []; + $Points[] = $X1; + $Points[] = $Y1; + $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX2; + $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY2; + $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX2; + $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY2; + $Points[] = $X1; + $Points[] = $Y1; + + /* Visual correction */ + if ($Angle == 180 || $Angle == 360) { + $Points[4] = $Points[2]; + } + if ($Angle == 90 || $Angle == 270) { + $Points[5] = $Points[3]; + } + + $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha); + $this->imageFilledPolygonWrapper($this->Picture, $Points, 4, $ArrowColor); + + $this->drawLine( + $Points[0], + $Points[1], + $Points[2], + $Points[3], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + $this->drawLine( + $Points[2], + $Points[3], + $Points[4], + $Points[5], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + $this->drawLine( + $Points[0], + $Points[1], + $Points[4], + $Points[5], + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + + $this->drawLine( + $TailX, + $TailY, + $TailX2, + $TailY2, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + } else { + $this->drawLine( + $X1, + $Y1, + $TailX, + $TailY, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + } + /* Re-enable shadows */ + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a label with associated arrow + * @param int $X1 + * @param int $Y1 + * @param string $Text + * @param array $Format + */ + public function drawArrowLabel($X1, $Y1, $Text, array $Format = []) + { + $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; + $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; + $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; + $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Length = isset($Format["Length"]) ? $Format["Length"] : 50; + $Angle = isset($Format["Angle"]) ? ((int) $Format["Angle"]) : 315; + $Size = isset($Format["Size"]) ? $Format["Size"] : 10; + $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP; + $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : false; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + + $Angle = $Angle % 360; + + $X2 = sin(($Angle + 180) * PI / 180) * $Length + $X1; + $Y2 = cos(($Angle + 180) * PI / 180) * $Length + $Y1; + + if ($RoundPos && $Angle > 0 && $Angle < 180) { + $Y2 = ceil($Y2); + } + if ($RoundPos && $Angle > 180) { + $Y2 = floor($Y2); + } + + $this->drawArrow($X2, $Y2, $X1, $Y1, $Format); + + $Size = imagettfbbox($FontSize, 0, $FontName, $Text); + $TxtWidth = max(abs($Size[2] - $Size[0]), abs($Size[0] - $Size[6])); + + if ($Angle > 0 && $Angle < 180) { + $this->drawLine( + $X2, + $Y2, + $X2 - $TxtWidth, + $Y2, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + if ($Position == POSITION_TOP) { + $this->drawText( + $X2, + $Y2 - 2, + $Text, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $Alpha, + "Align" => TEXT_ALIGN_BOTTOMRIGHT + ] + ); + } else { + $this->drawText( + $X2, + $Y2 + 4, + $Text, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $Alpha, + "Align" => TEXT_ALIGN_TOPRIGHT + ] + ); + } + } else { + $this->drawLine( + $X2, + $Y2, + $X2 + $TxtWidth, + $Y2, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks] + ); + if ($Position == POSITION_TOP) { + $this->drawText( + $X2, + $Y2 - 2, + $Text, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha] + ); + } else { + $this->drawText( + $X2, + $Y2 + 4, + $Text, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $Alpha, + "Align" => TEXT_ALIGN_TOPLEFT + ] + ); + } + } + } + + /** + * Draw a progress bar filled with specified % + * @param int $X + * @param int $Y + * @param int|float $Percent + * @param array $Format + */ + public function drawProgress($X, $Y, $Percent, array $Format = []) + { + if ($Percent > 100) { + $Percent = 100; + } + if ($Percent < 0) { + $Percent = 0; + } + + $Width = isset($Format["Width"]) ? $Format["Width"] : 200; + $Height = isset($Format["Height"]) ? $Format["Height"] : 20; + $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL; + $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : false; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10; + $R = isset($Format["R"]) ? $Format["R"] : 130; + $G = isset($Format["G"]) ? $Format["G"] : 130; + $B = isset($Format["B"]) ? $Format["B"] : 130; + $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1; + $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1; + $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0; + $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255; + $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255; + $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : null; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : false; + + if ($RFade != -1 && $GFade != -1 && $BFade != -1) { + $RFade = (($RFade - $R) / 100) * $Percent + $R; + $GFade = (($GFade - $G) / 100) * $Percent + $G; + $BFade = (($BFade - $B) / 100) * $Percent + $B; + } + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + if ($BoxSurrounding != null) { + $BoxBorderR = $BoxBackR + $Surrounding; + $BoxBorderG = $BoxBackG + $Surrounding; + $BoxBorderB = $BoxBackB + $Surrounding; + } + + if ($Orientation == ORIENTATION_VERTICAL) { + $InnerHeight = (($Height - 2) / 100) * $Percent; + $this->drawFilledRectangle( + $X, + $Y, + $X + $Width, + $Y - $Height, + [ + "R" => $BoxBackR, + "G" => $BoxBackG, + "B" => $BoxBackB, + "BorderR" => $BoxBorderR, + "BorderG" => $BoxBorderG, + "BorderB" => $BoxBorderB, + "NoAngle" => $NoAngle + ] + ); + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + if ($RFade != -1 && $GFade != -1 && $BFade != -1) { + $GradientOptions = [ + "StartR" => $RFade, + "StartG" => $GFade, + "StartB" => $BFade, + "EndR" => $R, + "EndG" => $G, + "EndB" => $B + ]; + $this->drawGradientArea( + $X + 1, + $Y - 1, + $X + $Width - 1, + $Y - $InnerHeight, + DIRECTION_VERTICAL, + $GradientOptions + ); + + if ($Surrounding) { + $this->drawRectangle( + $X + 1, + $Y - 1, + $X + $Width - 1, + $Y - $InnerHeight, + ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding] + ); + } + } else { + $this->drawFilledRectangle( + $X + 1, + $Y - 1, + $X + $Width - 1, + $Y - $InnerHeight, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } + $this->Shadow = $RestoreShadow; + + if ($ShowLabel && $LabelPos == LABEL_POS_BOTTOM) { + $this->drawText( + $X + ($Width / 2), + $Y + $Margin, + $Percent . "%", + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_TOP) { + $this->drawText( + $X + ($Width / 2), + $Y - $Height - $Margin, + $Percent . "%", + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) { + $this->drawText( + $X + ($Width / 2), + $Y - $InnerHeight - $Margin, + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLELEFT, "Angle" => 90] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) { + $this->drawText( + $X + ($Width / 2), + $Y - ($Height / 2), + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLEMIDDLE, "Angle" => 90] + ); + } + } else { + if ($Percent == 100) { + $InnerWidth = $Width - 1; + } else { + $InnerWidth = (($Width - 2) / 100) * $Percent; + } + $this->drawFilledRectangle( + $X, + $Y, + $X + $Width, + $Y + $Height, + [ + "R" => $BoxBackR, + "G" => $BoxBackG, + "B" => $BoxBackB, + "BorderR" => $BoxBorderR, + "BorderG" => $BoxBorderG, + "BorderB" => $BoxBorderB, + "NoAngle" => $NoAngle + ] + ); + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + if ($RFade != -1 && $GFade != -1 && $BFade != -1) { + $GradientOptions = [ + "StartR" => $R, + "StartG" => $G, + "StartB" => $B, + "EndR" => $RFade, + "EndG" => $GFade, + "EndB" => $BFade + ]; + $this->drawGradientArea( + $X + 1, + $Y + 1, + $X + $InnerWidth, + $Y + $Height - 1, + DIRECTION_HORIZONTAL, + $GradientOptions + ); + + if ($Surrounding) { + $this->drawRectangle( + $X + 1, + $Y + 1, + $X + $InnerWidth, + $Y + $Height - 1, + ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding] + ); + } + } else { + $this->drawFilledRectangle( + $X + 1, + $Y + 1, + $X + $InnerWidth, + $Y + $Height - 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB + ] + ); + } + $this->Shadow = $RestoreShadow; + + if ($ShowLabel && $LabelPos == LABEL_POS_LEFT) { + $this->drawText( + $X - $Margin, + $Y + ($Height / 2), + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLERIGHT] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_RIGHT) { + $this->drawText( + $X + $Width + $Margin, + $Y + ($Height / 2), + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) { + $this->drawText( + $X + ($Width / 2), + $Y + ($Height / 2), + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLEMIDDLE] + ); + } + if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) { + $this->drawText( + $X + $InnerWidth + $Margin, + $Y + ($Height / 2), + $Percent . "%", + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + } + } + } + + /** + * Draw the legend of the active series + * @param int $X + * @param int $Y + * @param array $Format + */ + public function drawLegend($X, $Y, array $Format = []) + { + $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; + $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + $Data = $this->DataSet->getData(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true + && $SerieName != $Data["Abscissa"] + && isset($Serie["Picture"]) + ) { + list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]); + if ($IconAreaWidth < $PicWidth) { + $IconAreaWidth = $PicWidth; + } + if ($IconAreaHeight < $PicHeight) { + $IconAreaHeight = $PicHeight; + } + } + } + + $YStep = max($this->FontSize, $IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $Boundaries = []; + $Boundaries["L"] = $X; + $Boundaries["T"] = $Y; + $Boundaries["R"] = 0; + $Boundaries["B"] = 0; + $vY = $Y; + $vX = $X; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + if ($Mode == LEGEND_VERTICAL) { + $BoxArray = $this->getTextBox( + $vX + $IconAreaWidth + 4, + $vY + $IconAreaHeight / 2, + $FontName, + $FontSize, + 0, + $Serie["Description"] + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Lines = preg_split("/\n/", $Serie["Description"]); + $vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Serie["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->getTextBox( + $vX + $IconAreaWidth + 6, + $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key), + $FontName, + $FontSize, + 0, + $Value + ); + + if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { + $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; + } + if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { + $Boundaries["R"] = $BoxArray[1]["X"] + 2; + } + if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { + $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; + } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX = max($Width) + $XStep; + } + } + } + $vY = $vY - $YStep; + $vX = $vX - $XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) { + $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset; + } + + if ($Style == LEGEND_ROUND) { + $this->drawRoundedFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } elseif ($Style == LEGEND_BOX) { + $this->drawFilledRectangle( + $Boundaries["L"] - $Margin, + $Boundaries["T"] - $Margin, + $Boundaries["R"] + $Margin, + $Boundaries["B"] + $Margin, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ] + ); + } + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Ticks = $Serie["Ticks"]; + $Weight = $Serie["Weight"]; + + if (isset($Serie["Picture"])) { + $Picture = $Serie["Picture"]; + list($PicWidth, $PicHeight) = $this->getPicInfo($Picture); + $PicX = $X + $IconAreaWidth / 2; + $PicY = $Y + $IconAreaHeight / 2; + + $this->drawFromPNG($PicX - $PicWidth / 2, $PicY - $PicHeight / 2, $Picture); + } else { + if ($Family == LEGEND_FAMILY_BOX) { + $XOffset = 0; + if ($BoxWidth != $IconAreaWidth) { + $XOffset = floor(($IconAreaWidth - $BoxWidth) / 2); + } + $YOffset = 0; + if ($BoxHeight != $IconAreaHeight) { + $YOffset = floor(($IconAreaHeight - $BoxHeight) / 2); + } + + $this->drawFilledRectangle( + $X + 1 + $XOffset, + $Y + 1 + $YOffset, + $X + $BoxWidth + $XOffset + 1, + $Y + $BoxHeight + 1 + $YOffset, + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20] + ); + $this->drawFilledRectangle( + $X + $XOffset, + $Y + $YOffset, + $X + $BoxWidth + $XOffset, + $Y + $BoxHeight + $YOffset, + ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20] + ); + } elseif ($Family == LEGEND_FAMILY_CIRCLE) { + $this->drawFilledCircle( + $X + 1 + $IconAreaWidth / 2, + $Y + 1 + $IconAreaHeight / 2, + min($IconAreaHeight / 2, $IconAreaWidth / 2), + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20] + ); + $this->drawFilledCircle( + $X + $IconAreaWidth / 2, + $Y + $IconAreaHeight / 2, + min($IconAreaHeight / 2, $IconAreaWidth / 2), + ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20] + ); + } elseif ($Family == LEGEND_FAMILY_LINE) { + $this->drawLine( + $X + 1, + $Y + 1 + $IconAreaHeight / 2, + $X + 1 + $IconAreaWidth, + $Y + 1 + $IconAreaHeight / 2, + ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20, "Ticks" => $Ticks, "Weight" => $Weight] + ); + $this->drawLine( + $X, + $Y + $IconAreaHeight / 2, + $X + $IconAreaWidth, + $Y + $IconAreaHeight / 2, + ["R" => $R, "G" => $G, "B" => $B, "Ticks" => $Ticks, "Weight" => $Weight] + ); + } + } + + if ($Mode == LEGEND_VERTICAL) { + $Lines = preg_split("/\n/", $Serie["Description"]); + foreach ($Lines as $Key => $Value) { + $this->drawText( + $X + $IconAreaWidth + 4, + $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key), + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT, + "FontSize" => $FontSize, + "FontName" => $FontName + ] + ); + } + $Y = $Y + max($this->FontSize * count($Lines), $IconAreaHeight) + 5; + } elseif ($Mode == LEGEND_HORIZONTAL) { + $Lines = preg_split("/\n/", $Serie["Description"]); + $Width = []; + foreach ($Lines as $Key => $Value) { + $BoxArray = $this->drawText( + $X + $IconAreaWidth + 4, + $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key), + $Value, + [ + "R" => $FontR, + "G" => $FontG, + "B" => $FontB, + "Align" => TEXT_ALIGN_MIDDLELEFT, + "FontSize" => $FontSize, + "FontName" => $FontName + ] + ); + $Width[] = $BoxArray[1]["X"]; + } + $X = max($Width) + 2 + $XStep; + } + } + } + + + $this->Shadow = $RestoreShadow; + } + + /** + * @param array $Format + * @throws Exception + */ + public function drawScale(array $Format = []) + { + $FloatingOffset = 0; + $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT; + $Floating = isset($Format["Floating"]) ? $Format["Floating"] : false; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; + $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : false; + $RemoveYAxis = isset($Format["RemoveYAxis"]) ? $Format["RemoveYAxis"] : false; + $RemoveYAxiValues = isset($Format["RemoveYAxisValues"]) ? $Format["RemoveYAxisValues"] : false; + $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; + $Factors = isset($Format["Factors"]) ? $Format["Factors"] : [1, 2, 5]; + $ManualScale = isset($Format["ManualScale"]) + ? $Format["ManualScale"] : ["0" => ["Min" => -100, "Max" => 100]] + ; + $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO; + $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; + $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; + $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; + $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; + $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : true; + $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; + $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; + $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; + $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; + $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; + $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; + $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; + $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; + $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; + $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; + $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; + $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; + $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; + $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : false; + $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; + $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; + $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; + $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; + $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; + $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; + $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : true; + $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; + $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : false; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; + $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : false; + $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; + $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; + $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; + $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20; + $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; + $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; + $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; + $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20; + $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL; + $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0; + $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0; + $RemoveSkippedAxis = isset($Format["RemoveSkippedAxis"]) ? $Format["RemoveSkippedAxis"] : false; + $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks + 2; + $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR; + $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG; + $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB; + $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha - 30; + $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo; + $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo; + $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo; + $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha - 80; + $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0; + $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2; + + /* Floating scale require X & Y margins to be set manually */ + if ($Floating && ($XMargin == AUTO || $YMargin == 0)) { + $Floating = false; + } + + /* Skip a NOTICE event in case of an empty array */ + if ($DrawYLines == NONE || $DrawYLines == false) { + $DrawYLines = ["zarma" => "31"]; + } + + /* Define the color for the skipped elements */ + $SkippedAxisColor = [ + "R" => $SkippedAxisR, + "G" => $SkippedAxisG, + "B" => $SkippedAxisB, + "Alpha" => $SkippedAxisAlpha, + "Ticks" => $SkippedAxisTicks + ]; + $SkippedTickColor = [ + "R" => $SkippedTickR, + "G" => $SkippedTickG, + "B" => $SkippedTickB, + "Alpha" => $SkippedTickAlpha + ]; + + $Data = $this->DataSet->getData(); + $Abscissa = null; + if (isset($Data["Abscissa"])) { + $Abscissa = $Data["Abscissa"]; + } + + /* Unset the abscissa axis, needed if we display multiple charts on the same picture */ + if ($Abscissa != null) { + foreach ($Data["Axis"] as $AxisID => $Parameters) { + if ($Parameters["Identity"] == AXIS_X) { + unset($Data["Axis"][$AxisID]); + } + } + } + + /* Build the scale settings */ + $GotXAxis = false; + foreach ($Data["Axis"] as $AxisID => $AxisParameter) { + if ($AxisParameter["Identity"] == AXIS_X) { + $GotXAxis = true; + } + + if ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y) { + $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $YMargin * 2; + } elseif ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X) { + $Height = $this->GraphAreaX2 - $this->GraphAreaX1; + } elseif ($Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y) { + $Height = $this->GraphAreaX2 - $this->GraphAreaX1 - $YMargin * 2; + ; + } else { + $Height = $this->GraphAreaY2 - $this->GraphAreaY1; + } + + $AxisMin = ABSOLUTE_MAX; + $AxisMax = OUT_OF_SIGHT; + if ($Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0) { + foreach ($Data["Series"] as $SerieID => $SerieParameter) { + if ($SerieParameter["Axis"] == $AxisID + && $Data["Series"][$SerieID]["isDrawable"] + && $Data["Abscissa"] != $SerieID + ) { + $AxisMax = max($AxisMax, $Data["Series"][$SerieID]["Max"]); + $AxisMin = min($AxisMin, $Data["Series"][$SerieID]["Min"]); + } + } + $AutoMargin = (($AxisMax - $AxisMin) / 100) * $XReleasePercent; + + $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin; + $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin; + if ($Mode == SCALE_MODE_START0) { + $Data["Axis"][$AxisID]["Min"] = 0; + } + } elseif ($Mode == SCALE_MODE_MANUAL) { + if (isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"])) { + $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; + $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; + } else { + throw new Exception("Manual scale boundaries not set."); + } + } elseif ($Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0) { + $Series = []; + foreach ($Data["Series"] as $SerieID => $SerieParameter) { + if ($SerieParameter["Axis"] == $AxisID + && $SerieParameter["isDrawable"] + && $Data["Abscissa"] != $SerieID + ) { + $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]); + } + } + + for ($ID = 0; $ID <= max($Series) - 1; $ID++) { + $PointMin = 0; + $PointMax = 0; + foreach ($Series as $SerieID => $ValuesCount) { + if (isset($Data["Series"][$SerieID]["Data"][$ID]) + && $Data["Series"][$SerieID]["Data"][$ID] != null + ) { + $Value = $Data["Series"][$SerieID]["Data"][$ID]; + if ($Value > 0) { + $PointMax = $PointMax + $Value; + } else { + $PointMin = $PointMin + $Value; + } + } + } + $AxisMax = max($AxisMax, $PointMax); + $AxisMin = min($AxisMin, $PointMin); + } + $AutoMargin = (($AxisMax - $AxisMin) / 100) * $XReleasePercent; + $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin; + $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin; + } + $MaxDivs = floor($Height / $MinDivHeight); + + if ($Mode == SCALE_MODE_ADDALL_START0) { + $Data["Axis"][$AxisID]["Min"] = 0; + } + + $Scale = $this->computeScale( + $Data["Axis"][$AxisID]["Min"], + $Data["Axis"][$AxisID]["Max"], + $MaxDivs, + $Factors, + $AxisID + ); + + $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin; + $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; + $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; + $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; + $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; + + if (isset($Scale["Format"])) { + $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; + } + if (!isset($Data["Axis"][$AxisID]["Display"])) { + $Data["Axis"][$AxisID]["Display"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Format"])) { + $Data["Axis"][$AxisID]["Format"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Unit"])) { + $Data["Axis"][$AxisID]["Unit"] = null; + } + } + + /* Still no X axis */ + if ($GotXAxis == false) { + if ($Abscissa != null) { + $Points = count($Data["Series"][$Abscissa]["Data"]); + $AxisName = null; + if ($AutoAxisLabels) { + $AxisName = isset($Data["Series"][$Abscissa]["Description"]) + ? $Data["Series"][$Abscissa]["Description"] : null + ; + } + } else { + $Points = 0; + $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : null; + foreach ($Data["Series"] as $SerieID => $SerieParameter) { + if ($SerieParameter["isDrawable"]) { + $Points = max($Points, count($SerieParameter["Data"])); + } + } + } + + $AxisID = count($Data["Axis"]); + $Data["Axis"][$AxisID]["Identity"] = AXIS_X; + if ($Pos == SCALE_POS_LEFTRIGHT) { + $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM; + } else { + $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; + } + if (isset($Data["AbscissaName"])) { + $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"]; + } + if ($XMargin == AUTO) { + if ($Pos == SCALE_POS_LEFTRIGHT) { + $Height = $this->GraphAreaX2 - $this->GraphAreaX1; + } else { + $Height = $this->GraphAreaY2 - $this->GraphAreaY1; + } + + if ($Points == 0 || $Points == 1) { + $Data["Axis"][$AxisID]["Margin"] = $Height / 2; + } else { + $Data["Axis"][$AxisID]["Margin"] = ($Height / $Points) / 2; + } + } else { + $Data["Axis"][$AxisID]["Margin"] = $XMargin; + } + $Data["Axis"][$AxisID]["Rows"] = $Points - 1; + if (!isset($Data["Axis"][$AxisID]["Display"])) { + $Data["Axis"][$AxisID]["Display"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Format"])) { + $Data["Axis"][$AxisID]["Format"] = null; + } + if (!isset($Data["Axis"][$AxisID]["Unit"])) { + $Data["Axis"][$AxisID]["Unit"] = null; + } + } + + /* Do we need to reverse the abscissa position? */ + if ($Pos != SCALE_POS_LEFTRIGHT) { + $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT; + if ($Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM) { + $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT; + } + } + $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"]; + + $this->DataSet->saveOrientation($Pos); + $this->DataSet->saveAxisConfig($Data["Axis"]); + $this->DataSet->saveYMargin($YMargin); + + $FontColorRo = $this->FontColorR; + $FontColorGo = $this->FontColorG; + $FontColorBo = $this->FontColorB; + + $AxisPos["L"] = $this->GraphAreaX1; + $AxisPos["R"] = $this->GraphAreaX2; + $AxisPos["T"] = $this->GraphAreaY1; + $AxisPos["B"] = $this->GraphAreaY2; + foreach ($Data["Axis"] as $AxisID => $Parameters) { + if (isset($Parameters["Color"])) { + $AxisR = $Parameters["Color"]["R"]; + $AxisG = $Parameters["Color"]["G"]; + $AxisB = $Parameters["Color"]["B"]; + $TickR = $Parameters["Color"]["R"]; + $TickG = $Parameters["Color"]["G"]; + $TickB = $Parameters["Color"]["B"]; + $this->setFontProperties( + [ + "R" => $Parameters["Color"]["R"], + "G" => $Parameters["Color"]["G"], + "B" => $Parameters["Color"]["B"] + ] + ); + } else { + $AxisR = $AxisRo; + $AxisG = $AxisGo; + $AxisB = $AxisBo; + $TickR = $TickRo; + $TickG = $TickGo; + $TickB = $TickBo; + $this->setFontProperties(["R" => $FontColorRo, "G" => $FontColorGo, "B" => $FontColorBo]); + } + + $LastValue = "w00t"; + $ID = 1; + if ($Parameters["Identity"] == AXIS_X) { + if ($Pos == SCALE_POS_LEFTRIGHT) { + if ($Parameters["Position"] == AXIS_POSITION_BOTTOM) { + if ($LabelRotation == 0) { + $LabelAlign = TEXT_ALIGN_TOPMIDDLE; + $YLabelOffset = 2; + } + if ($LabelRotation > 0 && $LabelRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $YLabelOffset = 5; + } + if ($LabelRotation == 180) { + $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; + $YLabelOffset = 5; + } + if ($LabelRotation > 180 && $LabelRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $YLabelOffset = 2; + } + + if (!$RemoveXAxis) { + if ($Floating) { + $FloatingOffset = $YMargin; + $this->drawLine( + $this->GraphAreaX1 + $Parameters["Margin"], + $AxisPos["B"], + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $this->GraphAreaX1, + $AxisPos["B"], + $this->GraphAreaX2, + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["B"], + $this->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["B"], + ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize] + ); + } + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2; + + if ($Parameters["Rows"] == 0) { + $Step = $Width; + } else { + $Step = $Width / ($Parameters["Rows"]); + } + + $MaxBottom = $AxisPos["B"]; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i; + $YPos = $AxisPos["B"]; + + if ($Abscissa != null) { + $Value = ""; + if (isset($Data["Series"][$Abscissa]["Data"][$i])) { + $Value = $this->scaleFormat( + $Data["Series"][$Abscissa]["Data"][$i], + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } else { + $Value = $i; + if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) { + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } + + $ID++; + $Skipped = true; + if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) + && !$RemoveXAxis + ) { + $Bounds = $this->drawText( + $XPos, + $YPos + $OuterTickWidth + $YLabelOffset, + $Value, + ["Angle" => $LabelRotation, "Align" => $LabelAlign] + ); + $TxtBottom = $YPos + $OuterTickWidth + 2 + ($Bounds[0]["Y"] - $Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom, $TxtBottom); + $LastValue = $Value; + $Skipped = false; + } + + if ($RemoveXAxis) { + $Skipped = false; + } + + if ($Skipped) { + if ($DrawXLines && !$RemoveSkippedAxis) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + $SkippedAxisColor + ); + } + if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) + && !$RemoveXAxis + && !$RemoveSkippedAxis + ) { + $this->drawLine( + $XPos, + $YPos - $SkippedInnerTickWidth, + $XPos, + $YPos + $SkippedOuterTickWidth, + $SkippedTickColor + ); + } + } else { + if ($DrawXLines + && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2) + ) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) { + $this->drawLine( + $XPos, + $YPos - $InnerTickWidth, + $XPos, + $YPos + $OuterTickWidth, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + } + } + } + + if (isset($Parameters["Name"]) && !$RemoveXAxis) { + $YPos = $MaxBottom + 2; + $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + $MaxBottom = $Bounds[0]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } elseif ($Parameters["Position"] == AXIS_POSITION_TOP) { + if ($LabelRotation == 0) { + $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; + $YLabelOffset = 2; + } + if ($LabelRotation > 0 && $LabelRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $YLabelOffset = 2; + } + if ($LabelRotation == 180) { + $LabelAlign = TEXT_ALIGN_TOPMIDDLE; + $YLabelOffset = 5; + } + if ($LabelRotation > 180 && $LabelRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $YLabelOffset = 5; + } + + if (!$RemoveXAxis) { + if ($Floating) { + $FloatingOffset = $YMargin; + $this->drawLine( + $this->GraphAreaX1 + $Parameters["Margin"], + $AxisPos["T"], + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $this->GraphAreaX1, + $AxisPos["T"], + $this->GraphAreaX2, + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["T"], + $this->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["T"], + ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize] + ); + } + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2; + + if ($Parameters["Rows"] == 0) { + $Step = $Width; + } else { + $Step = $Width / $Parameters["Rows"]; + } + + $MinTop = $AxisPos["T"]; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i; + $YPos = $AxisPos["T"]; + + if ($Abscissa != null) { + $Value = ""; + if (isset($Data["Series"][$Abscissa]["Data"][$i])) { + $Value = $this->scaleFormat( + $Data["Series"][$Abscissa]["Data"][$i], + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } else { + $Value = $i; + if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) { + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } + + $ID++; + $Skipped = true; + if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) + && !$RemoveXAxis + ) { + $Bounds = $this->drawText( + $XPos, + $YPos - $OuterTickWidth - $YLabelOffset, + $Value, + ["Angle" => $LabelRotation, "Align" => $LabelAlign] + ); + $TxtBox = $YPos - $OuterTickWidth - 2 - ($Bounds[0]["Y"] - $Bounds[2]["Y"]); + $MinTop = min($MinTop, $TxtBox); + $LastValue = $Value; + $Skipped = false; + } + + if ($RemoveXAxis) { + $Skipped = false; + } + + if ($Skipped) { + if ($DrawXLines && !$RemoveSkippedAxis) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + $SkippedAxisColor + ); + } + if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) + && !$RemoveXAxis + && !$RemoveSkippedAxis + ) { + $this->drawLine( + $XPos, + $YPos + $SkippedInnerTickWidth, + $XPos, + $YPos - $SkippedOuterTickWidth, + $SkippedTickColor + ); + } + } else { + if ($DrawXLines) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) { + $this->drawLine( + $XPos, + $YPos + $InnerTickWidth, + $XPos, + $YPos - $OuterTickWidth, + [ + "R" => $TickR, + "G" => $TickG, + "B" => $TickB, + "Alpha" => $TickAlpha + ] + ); + } + } + } + + if (isset($Parameters["Name"]) && !$RemoveXAxis) { + $YPos = $MinTop - 2; + $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $MinTop = $Bounds[2]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } + } elseif ($Pos == SCALE_POS_TOPBOTTOM) { + if ($Parameters["Position"] == AXIS_POSITION_LEFT) { + if ($LabelRotation == 0) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $XLabelOffset = -2; + } + if ($LabelRotation > 0 && $LabelRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $XLabelOffset = -6; + } + if ($LabelRotation == 180) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $XLabelOffset = -2; + } + if ($LabelRotation > 180 && $LabelRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $XLabelOffset = -5; + } + + if (!$RemoveXAxis) { + if ($Floating) { + $FloatingOffset = $YMargin; + $this->drawLine( + $AxisPos["L"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["L"], + $this->GraphAreaY2 - $Parameters["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $AxisPos["L"], + $this->GraphAreaY1, + $AxisPos["L"], + $this->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $AxisPos["L"], + $this->GraphAreaY2 - $Parameters["Margin"], + $AxisPos["L"], + $this->GraphAreaY2 + ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2; + + if ($Parameters["Rows"] == 0) { + $Step = $Height; + } else { + $Step = $Height / $Parameters["Rows"]; + } + + $MinLeft = $AxisPos["L"]; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i; + $XPos = $AxisPos["L"]; + + if ($Abscissa != null) { + $Value = ""; + if (isset($Data["Series"][$Abscissa]["Data"][$i])) { + $Value = $this->scaleFormat( + $Data["Series"][$Abscissa]["Data"][$i], + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } else { + $Value = $i; + if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) { + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } + + $ID++; + $Skipped = true; + if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) + && !$RemoveXAxis + ) { + $Bounds = $this->drawText( + $XPos - $OuterTickWidth + $XLabelOffset, + $YPos, + $Value, + ["Angle" => $LabelRotation, "Align" => $LabelAlign] + ); + $TxtBox = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MinLeft = min($MinLeft, $TxtBox); + $LastValue = $Value; + $Skipped = false; + } + + if ($RemoveXAxis) { + $Skipped = false; + } + + if ($Skipped) { + if ($DrawXLines && !$RemoveSkippedAxis) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + $SkippedAxisColor + ); + } + if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) + && !$RemoveXAxis + && !$RemoveSkippedAxis + ) { + $this->drawLine( + $XPos - $SkippedOuterTickWidth, + $YPos, + $XPos + $SkippedInnerTickWidth, + $YPos, + $SkippedTickColor + ); + } + } else { + if ($DrawXLines && + ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2) + ) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) { + $this->drawLine( + $XPos - $OuterTickWidth, + $YPos, + $XPos + $InnerTickWidth, + $YPos, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + } + } + } + if (isset($Parameters["Name"]) && !$RemoveXAxis) { + $XPos = $MinLeft - 2; + $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90] + ); + $MinLeft = $Bounds[0]["X"]; + + $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) { + if ($LabelRotation == 0) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $XLabelOffset = 2; + } + if ($LabelRotation > 0 && $LabelRotation < 190) { + $LabelAlign = TEXT_ALIGN_MIDDLELEFT; + $XLabelOffset = 6; + } + if ($LabelRotation == 180) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $XLabelOffset = 5; + } + if ($LabelRotation > 180 && $LabelRotation < 360) { + $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; + $XLabelOffset = 7; + } + + if (!$RemoveXAxis) { + if ($Floating) { + $FloatingOffset = $YMargin; + $this->drawLine( + $AxisPos["R"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["R"], + $this->GraphAreaY2 - $Parameters["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $AxisPos["R"], + $this->GraphAreaY1, + $AxisPos["R"], + $this->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $AxisPos["R"], + $this->GraphAreaY2 - $Parameters["Margin"], + $AxisPos["R"], + $this->GraphAreaY2 + ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2; + + if ($Parameters["Rows"] == 0) { + $Step = $Height; + } else { + $Step = $Height / $Parameters["Rows"]; + } + + $MaxRight = $AxisPos["R"]; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i; + $XPos = $AxisPos["R"]; + + if ($Abscissa != null) { + $Value = ""; + if (isset($Data["Series"][$Abscissa]["Data"][$i])) { + $Value = $this->scaleFormat( + $Data["Series"][$Abscissa]["Data"][$i], + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } else { + $Value = $i; + if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) { + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Data["XAxisDisplay"], + $Data["XAxisFormat"], + $Data["XAxisUnit"] + ); + } + } + + $ID++; + $Skipped = true; + if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) + && !$RemoveXAxis + ) { + $Bounds = $this->drawText( + $XPos + $OuterTickWidth + $XLabelOffset, + $YPos, + $Value, + ["Angle" => $LabelRotation, "Align" => $LabelAlign] + ); + $TxtBox = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MaxRight = max($MaxRight, $TxtBox); + $LastValue = $Value; + $Skipped = false; + } + + if ($RemoveXAxis) { + $Skipped = false; + } + + if ($Skipped) { + if ($DrawXLines && !$RemoveSkippedAxis) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + $SkippedAxisColor + ); + } + if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) + && !$RemoveXAxis + && !$RemoveSkippedAxis + ) { + $this->drawLine( + $XPos + $SkippedOuterTickWidth, + $YPos, + $XPos - $SkippedInnerTickWidth, + $YPos, + $SkippedTickColor + ); + } + } else { + if ($DrawXLines) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) { + $this->drawLine( + $XPos + $OuterTickWidth, + $YPos, + $XPos - $InnerTickWidth, + $YPos, + [ + "R" => $TickR, + "G" => $TickG, + "B" => $TickB, + "Alpha" => $TickAlpha + ] + ); + } + } + } + + if (isset($Parameters["Name"]) && !$RemoveXAxis) { + $XPos = $MaxRight + 4; + $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270] + ); + $MaxRight = $Bounds[1]["X"]; + + $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize; + } + + $AxisPos["R"] = $MaxRight + $ScaleSpacing; + } + } + } + + if ($Parameters["Identity"] == AXIS_Y && !$RemoveYAxis) { + if ($Pos == SCALE_POS_LEFTRIGHT) { + if ($Parameters["Position"] == AXIS_POSITION_LEFT) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->drawLine( + $AxisPos["L"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["L"], + $this->GraphAreaY2 - $Parameters["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $AxisPos["L"], + $this->GraphAreaY1, + $AxisPos["L"], + $this->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $AxisPos["L"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["L"], + $this->GraphAreaY1 - ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2; + $Step = $Height / $Parameters["Rows"]; + $SubTicksSize = $Step / 2; + $MinLeft = $AxisPos["L"]; + $LastY = null; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i; + $XPos = $AxisPos["L"]; + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Parameters["Display"], + $Parameters["Format"], + $Parameters["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastY != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->drawFilledRectangle( + $this->GraphAreaX1 + $FloatingOffset, + $LastY, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + $BGColor + ); + } + + if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $Parameters["Rows"]) { + $this->drawLine( + $XPos - $OuterSubTickWidth, + $YPos - $SubTicksSize, + $XPos + $InnerSubTickWidth, + $YPos - $SubTicksSize, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + if (!$RemoveYAxiValues) { + $this->drawLine( + $XPos - $OuterTickWidth, + $YPos, + $XPos + $InnerTickWidth, + $YPos, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->drawText( + $XPos - $OuterTickWidth - 2, + $YPos, + $Value, + ["Align" => TEXT_ALIGN_MIDDLERIGHT] + ); + $TxtLeft = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MinLeft = min($MinLeft, $TxtLeft); + } + + $LastY = $YPos; + } + + if (isset($Parameters["Name"])) { + $XPos = $MinLeft - 2; + $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90] + ); + $MinLeft = $Bounds[2]["X"]; + + $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->drawLine( + $AxisPos["R"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["R"], + $this->GraphAreaY2 - $Parameters["Margin"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $AxisPos["R"], + $this->GraphAreaY1, + $AxisPos["R"], + $this->GraphAreaY2, + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $AxisPos["R"], + $this->GraphAreaY1 + $Parameters["Margin"], + $AxisPos["R"], + $this->GraphAreaY1 - ($ArrowSize * 2), + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2; + $Step = $Height / $Parameters["Rows"]; + $SubTicksSize = $Step / 2; + $MaxLeft = $AxisPos["R"]; + $LastY = null; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i; + $XPos = $AxisPos["R"]; + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Parameters["Display"], + $Parameters["Format"], + $Parameters["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastY != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->drawFilledRectangle( + $this->GraphAreaX1 + $FloatingOffset, + $LastY, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + $BGColor + ); + } + + if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) { + $this->drawLine( + $this->GraphAreaX1 + $FloatingOffset, + $YPos, + $this->GraphAreaX2 - $FloatingOffset, + $YPos, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $Parameters["Rows"]) { + $this->drawLine( + $XPos - $OuterSubTickWidth, + $YPos - $SubTicksSize, + $XPos + $InnerSubTickWidth, + $YPos - $SubTicksSize, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->drawLine( + $XPos - $InnerTickWidth, + $YPos, + $XPos + $OuterTickWidth, + $YPos, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->drawText( + $XPos + $OuterTickWidth + 2, + $YPos, + $Value, + ["Align" => TEXT_ALIGN_MIDDLELEFT] + ); + $TxtLeft = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]); + $MaxLeft = max($MaxLeft, $TxtLeft); + + $LastY = $YPos; + } + + if (isset($Parameters["Name"])) { + $XPos = $MaxLeft + 6; + $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270] + ); + $MaxLeft = $Bounds[2]["X"]; + + $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize; + } + $AxisPos["R"] = $MaxLeft + $ScaleSpacing; + } + } elseif ($Pos == SCALE_POS_TOPBOTTOM) { + if ($Parameters["Position"] == AXIS_POSITION_TOP) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->drawLine( + $this->GraphAreaX1 + $Parameters["Margin"], + $AxisPos["T"], + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $this->GraphAreaX1, + $AxisPos["T"], + $this->GraphAreaX2, + $AxisPos["T"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["T"], + $this->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["T"], + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2; + $Step = $Width / $Parameters["Rows"]; + $SubTicksSize = $Step / 2; + $MinTop = $AxisPos["T"]; + $LastX = null; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i; + $YPos = $AxisPos["T"]; + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Parameters["Display"], + $Parameters["Format"], + $Parameters["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastX != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->drawFilledRectangle( + $LastX, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + $BGColor + ); + } + + if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $Parameters["Rows"]) { + $this->drawLine( + $XPos + $SubTicksSize, + $YPos - $OuterSubTickWidth, + $XPos + $SubTicksSize, + $YPos + $InnerSubTickWidth, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->drawLine( + $XPos, + $YPos - $OuterTickWidth, + $XPos, + $YPos + $InnerTickWidth, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->drawText( + $XPos, + $YPos - $OuterTickWidth - 2, + $Value, + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $TxtHeight = $YPos - $OuterTickWidth - 2 - ($Bounds[1]["Y"] - $Bounds[2]["Y"]); + $MinTop = min($MinTop, $TxtHeight); + + $LastX = $XPos; + } + + if (isset($Parameters["Name"])) { + $YPos = $MinTop - 2; + $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $MinTop = $Bounds[2]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } elseif ($Parameters["Position"] == AXIS_POSITION_BOTTOM) { + if ($Floating) { + $FloatingOffset = $XMargin; + $this->drawLine( + $this->GraphAreaX1 + $Parameters["Margin"], + $AxisPos["B"], + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } else { + $FloatingOffset = 0; + $this->drawLine( + $this->GraphAreaX1, + $AxisPos["B"], + $this->GraphAreaX2, + $AxisPos["B"], + ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha] + ); + } + + if ($DrawArrows) { + $this->drawArrow( + $this->GraphAreaX2 - $Parameters["Margin"], + $AxisPos["B"], + $this->GraphAreaX2 + ($ArrowSize * 2), + $AxisPos["B"], + [ + "FillR" => $AxisR, + "FillG" => $AxisG, + "FillB" => $AxisB, + "Size" => $ArrowSize + ] + ); + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2; + $Step = $Width / $Parameters["Rows"]; + $SubTicksSize = $Step / 2; + $MaxBottom = $AxisPos["B"]; + $LastX = null; + for ($i = 0; $i <= $Parameters["Rows"]; $i++) { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i; + $YPos = $AxisPos["B"]; + $Value = $this->scaleFormat( + $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, + $Parameters["Display"], + $Parameters["Format"], + $Parameters["Unit"] + ); + + if ($i % 2 == 1) { + $BGColor = [ + "R" => $BackgroundR1, + "G" => $BackgroundG1, + "B" => $BackgroundB1, + "Alpha" => $BackgroundAlpha1 + ]; + } else { + $BGColor = [ + "R" => $BackgroundR2, + "G" => $BackgroundG2, + "B" => $BackgroundB2, + "Alpha" => $BackgroundAlpha2 + ]; + } + if ($LastX != null + && $CycleBackground + && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) + ) { + $this->drawFilledRectangle( + $LastX, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + $BGColor + ); + } + + if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) { + $this->drawLine( + $XPos, + $this->GraphAreaY1 + $FloatingOffset, + $XPos, + $this->GraphAreaY2 - $FloatingOffset, + [ + "R" => $GridR, + "G" => $GridG, + "B" => $GridB, + "Alpha" => $GridAlpha, + "Ticks" => $GridTicks + ] + ); + } + + if ($DrawSubTicks && $i != $Parameters["Rows"]) { + $this->drawLine( + $XPos + $SubTicksSize, + $YPos - $OuterSubTickWidth, + $XPos + $SubTicksSize, + $YPos + $InnerSubTickWidth, + [ + "R" => $SubTickR, + "G" => $SubTickG, + "B" => $SubTickB, + "Alpha" => $SubTickAlpha + ] + ); + } + $this->drawLine( + $XPos, + $YPos - $OuterTickWidth, + $XPos, + $YPos + $InnerTickWidth, + ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha] + ); + $Bounds = $this->drawText( + $XPos, + $YPos + $OuterTickWidth + 2, + $Value, + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + $TxtHeight = $YPos + $OuterTickWidth + 2 + ($Bounds[1]["Y"] - $Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom, $TxtHeight); + + $LastX = $XPos; + } + + if (isset($Parameters["Name"])) { + $YPos = $MaxBottom + 2; + $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2; + $Bounds = $this->drawText( + $XPos, + $YPos, + $Parameters["Name"], + ["Align" => TEXT_ALIGN_TOPMIDDLE] + ); + $MaxBottom = $Bounds[0]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } + } + } + } + } + + /** + * Draw an X threshold + * @param mixed $Value + * @param boolean $Format + * @return array|null|integer + */ + public function drawXThreshold($Value, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : false; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : false; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : false; + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + $XScale = $this->scaleGetXSettings(); + + if (is_array($Value)) { + foreach ($Value as $Key => $ID) { + $this->drawXThreshold($ID, $Format); + } + return 0; + } + + if ($ValueIsLabel) { + $Format["ValueIsLabel"] = false; + foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue) { + if ($SerieValue == $Value) { + $this->drawXThreshold($Key, $Format); + } + } + return 0; + } + + $CaptionSettings = [ + "DrawBox" => $DrawBox, + "DrawBoxBorder" => $DrawBoxBorder, + "BorderOffset" => $BorderOffset, + "BoxRounded" => $BoxRounded, + "RoundedRadius" => $RoundedRadius, + "BoxR" => $BoxR, + "BoxG" => $BoxG, + "BoxB" => $BoxB, + "BoxAlpha" => $BoxAlpha, + "BoxSurrounding" => $BoxSurrounding, + "BoxBorderR" => $BoxBorderR, + "BoxBorderG" => $BoxBorderG, + "BoxBorderB" => $BoxBorderB, + "BoxBorderAlpha" => $BoxBorderAlpha, + "R" => $CaptionR, + "G" => $CaptionG, + "B" => $CaptionB, + "Alpha" => $CaptionAlpha + ]; + + if ($Caption == null) { + $Caption = $Value; + if (isset($Data["Abscissa"]) + && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value]) + ) { + $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value]; + } + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] * 2) / $XScale[1]; + $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value; + $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; + + if ($XPos >= $this->GraphAreaX1 + $AbscissaMargin + && $XPos <= $this->GraphAreaX2 - $AbscissaMargin + ) { + $this->drawLine( + $XPos, + $YPos1, + $XPos, + $YPos2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->drawLine( + $XPos - 1, + $YPos1, + $XPos - 1, + $YPos2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + $this->drawLine( + $XPos + 1, + $YPos1, + $XPos + 1, + $YPos2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $Y = $YPos1 + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + } else { + $Y = $YPos2 - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + } + $this->drawText($XPos, $Y, $Caption, $CaptionSettings); + } + + return ["X" => $XPos]; + } + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] * 2) / $XScale[1]; + $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value; + $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; + + if ($XPos >= $this->GraphAreaY1 + $AbscissaMargin + && $XPos <= $this->GraphAreaY2 - $AbscissaMargin + ) { + $this->drawLine( + $YPos1, + $XPos, + $YPos2, + $XPos, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->drawLine( + $YPos1, + $XPos - 1, + $YPos2, + $XPos - 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + $this->drawLine( + $YPos1, + $XPos + 1, + $YPos2, + $XPos + 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $Y = $YPos1 + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; + } else { + $Y = $YPos2 - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; + } + + $this->drawText($Y, $XPos, $Caption, $CaptionSettings); + } + + return ["X" => $XPos]; + } + } + } + + /** + * Draw an X threshold area + * @param mixed $Value1 + * @param mixed $Value2 + * @param array $Format + * @return array|null + */ + public function drawXThresholdArea($Value1, $Value2, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : true; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : true; + + $RestoreShadow = $this->Shadow; + if ($DisableShadowOnArea && $this->Shadow) { + $this->Shadow = false; + } + + if ($BorderAlpha > 100) { + $BorderAlpha = 100; + } + + $Data = $this->DataSet->getData(); + $XScale = $this->scaleGetXSettings(); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] * 2) / $XScale[1]; + $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1; + $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2; + $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; + + if ($XPos1 < $this->GraphAreaX1 + $XScale[0]) { + $XPos1 = $this->GraphAreaX1 + $XScale[0]; + } + if ($XPos1 > $this->GraphAreaX2 - $XScale[0]) { + $XPos1 = $this->GraphAreaX2 - $XScale[0]; + } + if ($XPos2 < $this->GraphAreaX1 + $XScale[0]) { + $XPos2 = $this->GraphAreaX1 + $XScale[0]; + } + if ($XPos2 > $this->GraphAreaX2 - $XScale[0]) { + $XPos2 = $this->GraphAreaX2 - $XScale[0]; + } + + $this->drawFilledRectangle( + $XPos1, + $YPos1, + $XPos2, + $YPos2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + + if ($Border) { + $this->drawLine( + $XPos1, + $YPos1, + $XPos1, + $YPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->drawLine( + $XPos2, + $YPos1, + $XPos2, + $YPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($XPos2 - $XPos1) / 2 + $XPos1; + $YPos = ($YPos2 - $YPos1) / 2 + $YPos1; + + if ($NameAngle == ZONE_NAME_ANGLE_AUTO) { + $TxtPos = $this->getTextBox( + $XPos, + $YPos, + $this->FontName, + $this->FontSize, + 0, + $AreaName + ); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + $NameAngle = 90; + if (abs($XPos2 - $XPos1) > $TxtWidth) { + $NameAngle = 0; + } + } + $this->Shadow = $RestoreShadow; + $this->drawText( + $XPos, + $YPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => $NameAngle, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $this->Shadow = false; + } + } + + $this->Shadow = $RestoreShadow; + return ["X1" => $XPos1, "X2" => $XPos2]; + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] * 2) / $XScale[1]; + $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1; + $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2; + $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; + + if ($XPos1 < $this->GraphAreaY1 + $XScale[0]) { + $XPos1 = $this->GraphAreaY1 + $XScale[0]; + } + if ($XPos1 > $this->GraphAreaY2 - $XScale[0]) { + $XPos1 = $this->GraphAreaY2 - $XScale[0]; + } + if ($XPos2 < $this->GraphAreaY1 + $XScale[0]) { + $XPos2 = $this->GraphAreaY1 + $XScale[0]; + } + if ($XPos2 > $this->GraphAreaY2 - $XScale[0]) { + $XPos2 = $this->GraphAreaY2 - $XScale[0]; + } + + $this->drawFilledRectangle( + $YPos1, + $XPos1, + $YPos2, + $XPos2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + + if ($Border) { + $this->drawLine( + $YPos1, + $XPos1, + $YPos2, + $XPos1, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->drawLine( + $YPos1, + $XPos2, + $YPos2, + $XPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($XPos2 - $XPos1) / 2 + $XPos1; + $YPos = ($YPos2 - $YPos1) / 2 + $YPos1; + + $this->Shadow = $RestoreShadow; + $this->drawText( + $YPos, + $XPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => 0, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $this->Shadow = false; + } + } + + $this->Shadow = $RestoreShadow; + return ["X1" => $XPos1, "X2" => $XPos2]; + } + } + + /** + * Draw an Y threshold with the computed scale + * @param mixed $Value + * @param array $Format + * @return array|int + */ + public function drawThreshold($Value, array $Format = []) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : false; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : false; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : false; + + if (is_array($Value)) { + foreach ($Value as $Key => $ID) { + $this->drawThreshold($ID, $Format); + } + return 0; + } + + $CaptionSettings = [ + "DrawBox" => $DrawBox, + "DrawBoxBorder" => $DrawBoxBorder, + "BorderOffset" => $BorderOffset, + "BoxRounded" => $BoxRounded, + "RoundedRadius" => $RoundedRadius, + "BoxR" => $BoxR, + "BoxG" => $BoxG, + "BoxB" => $BoxB, + "BoxAlpha" => $BoxAlpha, + "BoxSurrounding" => $BoxSurrounding, + "BoxBorderR" => $BoxBorderR, + "BoxBorderG" => $BoxBorderG, + "BoxBorderB" => $BoxBorderB, + "BoxBorderAlpha" => $BoxBorderAlpha, + "R" => $CaptionR, + "G" => $CaptionG, + "B" => $CaptionB, + "Alpha" => $CaptionAlpha + ]; + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + + if ($NoMargin) { + $AbscissaMargin = 0; + } + if (!isset($Data["Axis"][$AxisID])) { + return -1; + } + if ($Caption == null) { + $Caption = $Value; + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $YPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]); + if ($YPos >= $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"] + && $YPos <= $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] + ) { + $X1 = $this->GraphAreaX1 + $AbscissaMargin; + $X2 = $this->GraphAreaX2 - $AbscissaMargin; + + $this->drawLine( + $X1, + $YPos, + $X2, + $YPos, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->drawLine( + $X1, + $YPos - 1, + $X2, + $YPos - 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + $this->drawLine( + $X1, + $YPos + 1, + $X2, + $YPos + 1, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $X = $X1 + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; + } else { + $X = $X2 - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; + } + + $this->drawText($X, $YPos, $Caption, $CaptionSettings); + } + } + + return ["Y" => $YPos]; + } + + if ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + $XPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]); + if ($XPos >= $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + && $XPos <= $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"] + ) { + $Y1 = $this->GraphAreaY1 + $AbscissaMargin; + $Y2 = $this->GraphAreaY2 - $AbscissaMargin; + + $this->drawLine( + $XPos, + $Y1, + $XPos, + $Y2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + + if ($Wide) { + $this->drawLine( + $XPos - 1, + $Y1, + $XPos - 1, + $Y2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + $this->drawLine( + $XPos + 1, + $Y1, + $XPos + 1, + $Y2, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / $WideFactor, + "Ticks" => $Ticks + ] + ); + } + + if ($WriteCaption) { + if ($CaptionAlign == CAPTION_LEFT_TOP) { + $Y = $Y1 + $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + } else { + $Y = $Y2 - $CaptionOffset; + $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + } + + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + $this->drawText($XPos, $Y, $Caption, $CaptionSettings); + } + } + + return ["Y" => $XPos]; + } + } + + /** + * Draw a threshold with the computed scale + * @param mixed $Value1 + * @param mixed $Value2 + * @param array $Format + * @return array|int|null + */ + public function drawThresholdArea($Value1, $Value2, array $Format = []) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : true; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : true; + $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : false; + + if ($Value1 > $Value2) { + list($Value1, $Value2) = [$Value2, $Value1]; + } + + $RestoreShadow = $this->Shadow; + if ($DisableShadowOnArea && $this->Shadow) { + $this->Shadow = false; + } + + if ($BorderAlpha > 100) { + $BorderAlpha = 100; + } + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + + if ($NoMargin) { + $AbscissaMargin = 0; + } + if (!isset($Data["Axis"][$AxisID])) { + return -1; + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $XPos1 = $this->GraphAreaX1 + $AbscissaMargin; + $XPos2 = $this->GraphAreaX2 - $AbscissaMargin; + $YPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]); + $YPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]); + + if ($YPos1 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) { + $YPos1 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + } + if ($YPos1 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) { + $YPos1 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + } + if ($YPos2 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) { + $YPos2 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + } + if ($YPos2 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) { + $YPos2 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + } + + $this->drawFilledRectangle( + $XPos1, + $YPos1, + $XPos2, + $YPos2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + if ($Border) { + $this->drawLine( + $XPos1, + $YPos1, + $XPos2, + $YPos1, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->drawLine( + $XPos1, + $YPos2, + $XPos2, + $YPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($XPos2 - $XPos1) / 2 + $XPos1; + $YPos = ($YPos2 - $YPos1) / 2 + $YPos1; + $this->Shadow = $RestoreShadow; + $this->drawText( + $XPos, + $YPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => 0, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $this->Shadow = false; + } + } + + $this->Shadow = $RestoreShadow; + return ["Y1" => $YPos1, "Y2" => $YPos2]; + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + $YPos1 = $this->GraphAreaY1 + $AbscissaMargin; + $YPos2 = $this->GraphAreaY2 - $AbscissaMargin; + $XPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]); + $XPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]); + + if ($XPos1 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) { + $XPos1 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + } + if ($XPos1 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) { + $XPos1 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + } + if ($XPos2 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) { + $XPos2 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + } + if ($XPos2 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) { + $XPos2 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + } + + $this->drawFilledRectangle( + $XPos1, + $YPos1, + $XPos2, + $YPos2, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + if ($Border) { + $this->drawLine( + $XPos1, + $YPos1, + $XPos1, + $YPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + $this->drawLine( + $XPos2, + $YPos1, + $XPos2, + $YPos2, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Ticks" => $BorderTicks + ] + ); + } + + if ($AreaName != null) { + $XPos = ($YPos2 - $YPos1) / 2 + $YPos1; + $YPos = ($XPos2 - $XPos1) / 2 + $XPos1; + + if ($NameAngle == ZONE_NAME_ANGLE_AUTO) { + $TxtPos = $this->getTextBox( + $XPos, + $YPos, + $this->FontName, + $this->FontSize, + 0, + $AreaName + ); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + $NameAngle = 90; + if (abs($XPos2 - $XPos1) > $TxtWidth) { + $NameAngle = 0; + } + } + $this->Shadow = $RestoreShadow; + $this->drawText( + $YPos, + $XPos, + $AreaName, + [ + "R" => $NameR, + "G" => $NameG, + "B" => $NameB, + "Alpha" => $NameAlpha, + "Angle" => $NameAngle, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE + ] + ); + if ($DisableShadowOnArea) { + $this->Shadow = false; + } + } + + $this->Shadow = $RestoreShadow; + return ["Y1" => $XPos1, "Y2" => $XPos2]; + } + } + + /** + * Draw a plot chart + * @param array $Format + */ + public function drawPlotChart(array $Format = []) + { + $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : null; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + if (isset($Serie["Weight"])) { + $SerieWeight = $Serie["Weight"] + 2; + } else { + $SerieWeight = 2; + } + if ($PlotSize != null) { + $SerieWeight = $PlotSize; + } + + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = (int) $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + if (isset($Serie["Picture"])) { + $Picture = $Serie["Picture"]; + list($PicWidth, $PicHeight, $PicType) = $this->getPicInfo($Picture); + } else { + $Picture = null; + $PicOffset = 0; + } + + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Shape = $Serie["Shape"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + if ($Picture != null) { + $PicOffset = $PicHeight / 2; + $SerieWeight = 0; + } + $X = $this->GraphAreaX1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues) { + $this->drawText( + $X, + $Y - $DisplayOffset - $SerieWeight - $BorderSize - $PicOffset, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($Y != VOID) { + if ($RecordImageMap) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $SerieWeight, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ) + ); + } + + if ($Picture != null) { + $this->drawFromPicture( + $PicType, + $Picture, + $X - $PicWidth / 2, + $Y - $PicHeight / 2 + ); + } else { + $this->drawShape( + $X, + $Y, + $Shape, + $SerieWeight, + $PlotBorder, + $BorderSize, + $R, + $G, + $B, + $Alpha, + $BorderR, + $BorderG, + $BorderB, + $BorderAlpha + ); + } + } + $X = $X + $XStep; + } + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + if ($Picture != null) { + $PicOffset = $PicWidth / 2; + $SerieWeight = 0; + } + $Y = $this->GraphAreaY1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $X) { + if ($DisplayValues) { + $this->drawText( + $X + $DisplayOffset + $SerieWeight + $BorderSize + $PicOffset, + $Y, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "Angle" => 270, + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($X != VOID) { + if ($RecordImageMap) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $SerieWeight, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Picture != null) { + $this->drawFromPicture( + $PicType, + $Picture, + $X - $PicWidth / 2, + $Y - $PicHeight / 2 + ); + } else { + $this->drawShape( + $X, + $Y, + $Shape, + $SerieWeight, + $PlotBorder, + $BorderSize, + $R, + $G, + $B, + $Alpha, + $BorderR, + $BorderG, + $BorderB, + $BorderAlpha + ); + } + } + $Y = $Y + $YStep; + } + } + } + } + } + + /** + * Draw a spline chart + * @param array $Format + */ + public function drawSplineChart(array $Format = []) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : true; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null; // 234 + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null; // 55 + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null; // 26 + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + $Weight = $Serie["Weight"]; + + if ($BreakR == null) { + $BreakSettings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks + ]; + } else { + $BreakSettings = [ + "R" => $BreakR, + "G" => $BreakG, + "B" => $BreakB, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks, + "Weight" => $Weight + ]; + } + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $WayPoints = []; + $Force = $XStep / 5; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $LastX = 1; + $LastY = 1; + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues) { + $this->drawText( + $X, + $Y - $DisplayOffset, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($RecordImageMap && $Y != VOID) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Y == VOID && $LastY != null) { + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + $WayPoints = []; + } + + if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) { + $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings); + } + + if ($Y != VOID) { + $WayPoints[] = [$X, $Y]; + } + if ($Y != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($Y == VOID) { + $Y = null; + } + + $LastX = $X; + $LastY = $Y; + $X = $X + $XStep; + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $WayPoints = []; + $Force = $YStep / 5; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $LastX = 1; + $LastY = 1; + foreach ($PosArray as $Key => $X) { + if ($DisplayValues) { + $this->drawText( + $X + $DisplayOffset, + $Y, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "Angle" => 270, + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($RecordImageMap && $X != VOID) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($X == VOID && $LastX != null) { + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + $WayPoints = []; + } + + if ($X != VOID && $LastX == null && $LastGoodX != null && !$BreakVoid) { + $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings); + } + + if ($X != VOID) { + $WayPoints[] = [$X, $Y]; + } + if ($X != VOID) { + $LastGoodX = $X; + $LastGoodY = $Y; + } + if ($X == VOID) { + $X = null; + } + + $LastX = $X; + $LastY = $Y; + $Y = $Y + $YStep; + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + } + } + } + } + + /** + * Draw a filled spline chart + * @param array $Format + */ + public function drawFilledSplineChart(array $Format = []) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + if ($AroundZero) { + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + } + + if ($Threshold != null) { + foreach ($Threshold as $Key => $Params) { + $Threshold[$Key]["MinX"] = $this->scaleComputeY( + $Params["Min"], + ["AxisID" => $Serie["Axis"]] + ); + $Threshold[$Key]["MaxX"] = $this->scaleComputeY( + $Params["Max"], + ["AxisID" => $Serie["Axis"]] + ); + } + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $WayPoints = []; + $Force = $XStep / 5; + + if (!$AroundZero) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero < $this->GraphAreaY1 + 1) { + $YZero = $this->GraphAreaY1 + 1; + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues) { + $this->drawText( + $X, + $Y - $DisplayOffset, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($Y == VOID) { + $Area = $this->drawSpline( + $WayPoints, + ["Force" => $Force, "PathOnly" => true] + ); + + if (count($Area)) { + foreach ($Area as $key => $Points) { + $Corners = []; + $Corners[] = $Area[$key][0]["X"]; + $Corners[] = $YZero; + foreach ($Points as $subKey => $Point) { + if ($subKey == count($Points) - 1) { + $Corners[] = $Point["X"] - 1; + } else { + $Corners[] = $Point["X"]; + } + $Corners[] = $Point["Y"] + 1; + } + $Corners[] = $Points[$subKey]["X"] - 1; + $Corners[] = $YZero; + + $this->drawPolygonChart( + $Corners, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / 2, + "NoBorder" => true, + "Threshold" => $Threshold + ] + ); + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks + ] + ); + } + + $WayPoints = []; + } else { + $WayPoints[] = [$X, $Y - .5]; /* -.5 for AA visual fix */ + } + $X = $X + $XStep; + } + $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => true]); + + if (count($Area)) { + foreach ($Area as $key => $Points) { + $Corners = []; + $Corners[] = $Area[$key][0]["X"]; + $Corners[] = $YZero; + foreach ($Points as $subKey => $Point) { + if ($subKey == count($Points) - 1) { + $Corners[] = $Point["X"] - 1; + } else { + $Corners[] = $Point["X"]; + } + $Corners[] = $Point["Y"] + 1; + } + $Corners[] = $Points[$subKey]["X"] - 1; + $Corners[] = $YZero; + + $this->drawPolygonChart( + $Corners, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / 2, + "NoBorder" => true, + "Threshold" => $Threshold + ] + ); + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks + ] + ); + } + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $WayPoints = []; + $Force = $YStep / 5; + + if (!$AroundZero) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $X) { + if ($DisplayValues) { + $this->drawText( + $X + $DisplayOffset, + $Y, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "Angle" => 270, + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + if ($X == VOID) { + $Area = $this->drawSpline( + $WayPoints, + ["Force" => $Force, "PathOnly" => true] + ); + + if (count($Area)) { + foreach ($Area as $key => $Points) { + $Corners = []; + $Corners[] = $YZero; + $Corners[] = $Area[$key][0]["Y"]; + foreach ($Points as $subKey => $Point) { + if ($subKey == count($Points) - 1) { + $Corners[] = $Point["X"] - 1; + } else { + $Corners[] = $Point["X"]; + } + $Corners[] = $Point["Y"]; + } + $Corners[] = $YZero; + $Corners[] = $Points[$subKey]["Y"] - 1; + + $this->drawPolygonChart( + $Corners, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / 2, + "NoBorder" => true, + "Threshold" => $Threshold + ] + ); + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks + ] + ); + } + + $WayPoints = []; + } else { + $WayPoints[] = [$X, $Y]; + } + $Y = $Y + $YStep; + } + $Area = $this->drawSpline( + $WayPoints, + ["Force" => $Force, "PathOnly" => true] + ); + + if (count($Area)) { + foreach ($Area as $key => $Points) { + $Corners = []; + $Corners[] = $YZero; + $Corners[] = $Area[$key][0]["Y"]; + foreach ($Points as $subKey => $Point) { + if ($subKey == count($Points) - 1) { + $Corners[] = $Point["X"] - 1; + } else { + $Corners[] = $Point["X"]; + } + $Corners[] = $Point["Y"]; + } + $Corners[] = $YZero; + $Corners[] = $Points[$subKey]["Y"] - 1; + + $this->drawPolygonChart( + $Corners, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha / 2, + "NoBorder" => true, + "Threshold" => $Threshold + ] + ); + } + $this->drawSpline( + $WayPoints, + [ + "Force" => $Force, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks + ] + ); + } + } + } + } + } + + /** + * Draw a line chart + * @param array $Format + */ + public function drawLineChart(array $Format = []) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : true; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null; + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null; + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : false; + $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0; + $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0; + $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0; + $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + $Weight = $Serie["Weight"]; + + if ($ForceColor) { + $R = $ForceR; + $G = $ForceG; + $B = $ForceB; + $Alpha = $ForceAlpha; + } + + if ($BreakR == null) { + $BreakSettings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks, + "Weight" => $Weight + ]; + } else { + $BreakSettings = [ + "R" => $BreakR, + "G" => $BreakG, + "B" => $BreakB, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks, + "Weight" => $Weight + ]; + } + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($Serie["Data"][$Key] > 0) { + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_TOPMIDDLE; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X, + $Y - $Offset - $Weight, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => $Align + ] + ); + } + + if ($RecordImageMap && $Y != VOID) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Y != VOID && $LastX != null && $LastY != null) { + $this->drawLine( + $LastX, + $LastY, + $X, + $Y, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + } + if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) { + $this->drawLine( + $LastGoodX, + $LastGoodY, + $X, + $Y, + $BreakSettings + ); + $LastGoodY = null; + } + + if ($Y != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($Y == VOID) { + $Y = null; + } + + $LastX = $X; + $LastY = $Y; + $X = $X + $XStep; + } + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + foreach ($PosArray as $Key => $X) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + $this->drawText( + $X + $DisplayOffset + $Weight, + $Y, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "Angle" => 270, + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_BOTTOMMIDDLE + ] + ); + } + + if ($RecordImageMap && $X != VOID) { + $this->addToImageMap( + "CIRCLE", + floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($X != VOID && $LastX != null && $LastY != null) { + $this->drawLine( + $LastX, + $LastY, + $X, + $Y, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ] + ); + } + if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) { + $this->drawLine( + $LastGoodX, + $LastGoodY, + $X, + $Y, + $BreakSettings + ); + $LastGoodY = null; + } + + if ($X != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($X == VOID) { + $X = null; + } + + $LastX = $X; + $LastY = $Y; + $Y = $Y + $YStep; + } + } + } + } + } + + /** + * Draw a zone chart + * + * @param string $SerieA + * @param string $SerieB + * @param array $Format + * @return null|integer + */ + public function drawZoneChart($SerieA, $SerieB, array $Format = []) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50; + $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150; + $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + if (!isset($Data["Series"][$SerieA]["Data"]) + || !isset($Data["Series"][$SerieB]["Data"]) + ) { + return 0; + } + $SerieAData = $Data["Series"][$SerieA]["Data"]; + $SerieBData = $Data["Series"][$SerieB]["Data"]; + + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + + $PosArrayA = $this->scaleComputeY($SerieAData, ["AxisID" => $AxisID]); + $PosArrayB = $this->scaleComputeY($SerieBData, ["AxisID" => $AxisID]); + if (count($PosArrayA) != count($PosArrayB)) { + return 0; + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $LastX = null; + $LastY = null; + + $LastY1 = null; + $LastY2 = null; + $BoundsA = []; + $BoundsB = []; + foreach ($PosArrayA as $Key => $Y1) { + $Y2 = $PosArrayB[$Key]; + + $BoundsA[] = $X; + $BoundsA[] = $Y1; + $BoundsB[] = $X; + $BoundsB[] = $Y2; + + $LastX = $X; + $LastY1 = $Y1; + $LastY2 = $Y2; + + $X = $X + $XStep; + } + $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB)); + $this->drawPolygonChart( + $Bounds, + [ + "R" => $AreaR, + "G" => $AreaG, + "B" => $AreaB, + "Alpha" => $AreaAlpha + ] + ); + + for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) { + $this->drawLine( + $BoundsA[$i], + $BoundsA[$i + 1], + $BoundsA[$i + 2], + $BoundsA[$i + 3], + [ + "R" => $LineR, + "G" => $LineG, + "B" => $LineB, + "Alpha" => $LineAlpha, + "Ticks" => $LineTicks + ] + ); + $this->drawLine( + $BoundsB[$i], + $BoundsB[$i + 1], + $BoundsB[$i + 2], + $BoundsB[$i + 3], + [ + "R" => $LineR, + "G" => $LineG, + "B" => $LineB, + "Alpha" => $LineAlpha, + "Ticks" => $LineTicks + ] + ); + } + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $LastX = null; + $LastY = null; + + $LastX1 = null; + $LastX2 = null; + $BoundsA = []; + $BoundsB = []; + foreach ($PosArrayA as $Key => $X1) { + $X2 = $PosArrayB[$Key]; + + $BoundsA[] = $X1; + $BoundsA[] = $Y; + $BoundsB[] = $X2; + $BoundsB[] = $Y; + + $LastY = $Y; + $LastX1 = $X1; + $LastX2 = $X2; + + $Y = $Y + $YStep; + } + $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB)); + $this->drawPolygonChart( + $Bounds, + ["R" => $AreaR, "G" => $AreaG, "B" => $AreaB, "Alpha" => $AreaAlpha] + ); + + for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) { + $this->drawLine( + $BoundsA[$i], + $BoundsA[$i + 1], + $BoundsA[$i + 2], + $BoundsA[$i + 3], + [ + "R" => $LineR, + "G" => $LineG, + "B" => $LineB, + "Alpha" => $LineAlpha, + "Ticks" => $LineTicks + ] + ); + $this->drawLine( + $BoundsB[$i], + $BoundsB[$i + 1], + $BoundsB[$i + 2], + $BoundsB[$i + 3], + [ + "R" => $LineR, + "G" => $LineG, + "B" => $LineB, + "Alpha" => $LineAlpha, + "Ticks" => $LineTicks + ] + ); + } + } + } + + /** + * Draw a step chart + * @param array $Format + */ + public function drawStepChart(array $Format = []) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : false; + $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : true; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null; + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null; + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + $Weight = $Serie["Weight"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + if ($BreakR == null) { + $BreakSettings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks, + "Weight" => $Weight + ]; + } else { + $BreakSettings = [ + "R" => $BreakR, + "G" => $BreakG, + "B" => $BreakB, + "Alpha" => $Alpha, + "Ticks" => $VoidTicks, + "Weight" => $Weight + ]; + } + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + $Color = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ]; + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $Init = false; + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($Y <= $LastY) { + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_TOPMIDDLE; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X, + $Y - $Offset - $Weight, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align] + ); + } + + if ($Y != VOID && $LastX != null && $LastY != null) { + $this->drawLine($LastX, $LastY, $X, $LastY, $Color); + $this->drawLine($X, $LastY, $X, $Y, $Color); + if ($ReCenter && $X + $XStep < $this->GraphAreaX2 - $XMargin) { + $this->drawLine($X, $Y, $X + $XStep, $Y, $Color); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($X - $ImageMapPlotSize), + floor($Y - $ImageMapPlotSize), + floor($X + $XStep + $ImageMapPlotSize), + floor($Y + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } else { + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastX - $ImageMapPlotSize), + floor($LastY - $ImageMapPlotSize), + floor($X + $ImageMapPlotSize), + floor($LastY + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + } + + if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) { + if ($ReCenter) { + $this->drawLine($LastGoodX + $XStep, $LastGoodY, $X, $LastGoodY, $BreakSettings); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastGoodX + $XStep - $ImageMapPlotSize), + floor($LastGoodY - $ImageMapPlotSize), + floor($X + $ImageMapPlotSize), + floor($LastGoodY + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } else { + $this->drawLine($LastGoodX, $LastGoodY, $X, $LastGoodY, $BreakSettings); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastGoodX - $ImageMapPlotSize), + floor($LastGoodY - $ImageMapPlotSize), + floor($X + $ImageMapPlotSize), + floor($LastGoodY + $ImageMapPlotSize) + ), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + + $this->drawLine($X, $LastGoodY, $X, $Y, $BreakSettings); + $LastGoodY = null; + } elseif (!$BreakVoid && $LastGoodY == null && $Y != VOID) { + $this->drawLine($this->GraphAreaX1 + $XMargin, $Y, $X, $Y, $BreakSettings); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($this->GraphAreaX1 + $XMargin - $ImageMapPlotSize), + floor($Y - $ImageMapPlotSize), + floor($X + $ImageMapPlotSize), + floor($Y + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + + if ($Y != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($Y == VOID) { + $Y = null; + } + + if (!$Init && $ReCenter) { + $X = $X - $XStep / 2; + $Init = true; + } + $LastX = $X; + $LastY = $Y; + if ($LastX < $this->GraphAreaX1 + $XMargin) { + $LastX = $this->GraphAreaX1 + $XMargin; + } + $X = $X + $XStep; + } + if ($ReCenter) { + $this->drawLine($LastX, $LastY, $this->GraphAreaX2 - $XMargin, $LastY, $Color); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastX - $ImageMapPlotSize), + floor($LastY - $ImageMapPlotSize), + floor($this->GraphAreaX2 - $XMargin + $ImageMapPlotSize), + floor($LastY + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $Init = false; + foreach ($PosArray as $Key => $X) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($X >= $LastX) { + $Align = TEXT_ALIGN_MIDDLELEFT; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_MIDDLERIGHT; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X + $Offset + $Weight, + $Y, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => $Align + ] + ); + } + + if ($X != VOID && $LastX != null && $LastY != null) { + $this->drawLine($LastX, $LastY, $LastX, $Y, $Color); + $this->drawLine($LastX, $Y, $X, $Y, $Color); + + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastX - $ImageMapPlotSize), + floor($LastY - $ImageMapPlotSize), + floor($LastX + $XStep + $ImageMapPlotSize), + floor($Y + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + + if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) { + $this->drawLine( + $LastGoodX, + $LastGoodY, + $LastGoodX, + $LastGoodY + $YStep, + $Color + ); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastGoodX - $ImageMapPlotSize), + floor($LastGoodY - $ImageMapPlotSize), + floor($LastGoodX + $ImageMapPlotSize), + floor($LastGoodY + $YStep + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + $this->drawLine( + $LastGoodX, + $LastGoodY + $YStep, + $LastGoodX, + $Y, + $BreakSettings + ); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastGoodX - $ImageMapPlotSize), + floor($LastGoodY + $YStep - $ImageMapPlotSize), + floor($LastGoodX + $ImageMapPlotSize), + floor($YStep + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + $this->drawLine($LastGoodX, $Y, $X, $Y, $BreakSettings); + $LastGoodY = null; + } elseif ($X != VOID && $LastGoodY == null && !$BreakVoid) { + $this->drawLine($X, $this->GraphAreaY1 + $XMargin, $X, $Y, $BreakSettings); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($X - $ImageMapPlotSize), + floor($this->GraphAreaY1 + $XMargin - $ImageMapPlotSize), + floor($X + $ImageMapPlotSize), + floor($Y + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + + if ($X != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($X == VOID) { + $X = null; + } + + if (!$Init && $ReCenter) { + $Y = $Y - $YStep / 2; + $Init = true; + } + $LastX = $X; + $LastY = $Y; + if ($LastY < $this->GraphAreaY1 + $XMargin) { + $LastY = $this->GraphAreaY1 + $XMargin; + } + $Y = $Y + $YStep; + } + if ($ReCenter) { + $this->drawLine($LastX, $LastY, $LastX, $this->GraphAreaY2 - $XMargin, $Color); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + '%s,%s,%s,%s', + floor($LastX - $ImageMapPlotSize), + floor($LastY - $ImageMapPlotSize), + floor($LastX + $ImageMapPlotSize), + floor($this->GraphAreaY2 - $XMargin + $ImageMapPlotSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } + } + } + } + } + + /** + * Draw a step chart + * @param array $Format + */ + public function drawFilledStepChart(array $Format = []) + { + $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : true; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + + $Color = ["R" => $R, "G" => $G, "B" => $B]; + if ($ForceTransparency != null) { + $Color["Alpha"] = $ForceTransparency; + } else { + $Color["Alpha"] = $Alpha; + } + + $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]); + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero < $this->GraphAreaY1 + 1) { + $YZero = $this->GraphAreaY1 + 1; + } + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!$AroundZero) { + $YZero = $this->GraphAreaY2 - 1; + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $Points = []; + $Init = false; + foreach ($PosArray as $Key => $Y) { + if ($Y == VOID && $LastX != null && $LastY != null && count($Points)) { + $Points[] = $LastX; + $Points[] = $LastY; + $Points[] = $X; + $Points[] = $LastY; + $Points[] = $X; + $Points[] = $YZero; + $this->drawPolygon($Points, $Color); + $Points = []; + } + + if ($Y != VOID && $LastX != null && $LastY != null) { + if (count($Points)) { + $Points[] = $LastX; + $Points[] = $YZero; + } + $Points[] = $LastX; + $Points[] = $LastY; + $Points[] = $X; + $Points[] = $LastY; + $Points[] = $X; + $Points[] = $Y; + } + + if ($Y != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($Y == VOID) { + $Y = null; + } + + if (!$Init && $ReCenter) { + $X = $X - $XStep / 2; + $Init = true; + } + $LastX = $X; + $LastY = $Y; + if ($LastX < $this->GraphAreaX1 + $XMargin) { + $LastX = $this->GraphAreaX1 + $XMargin; + } + $X = $X + $XStep; + } + + if ($ReCenter) { + $Points[] = $LastX + $XStep / 2; + $Points[] = $LastY; + $Points[] = $LastX + $XStep / 2; + $Points[] = $YZero; + } else { + $Points[] = $LastX; + $Points[] = $YZero; + } + + $this->drawPolygon($Points, $Color); + } else { + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $LastGoodY = null; + $LastGoodX = null; + $Points = []; + foreach ($PosArray as $Key => $X) { + if ($X == VOID && $LastX != null && $LastY != null && count($Points)) { + $Points[] = $LastX; + $Points[] = $LastY; + $Points[] = $LastX; + $Points[] = $Y; + $Points[] = $YZero; + $Points[] = $Y; + $this->drawPolygon($Points, $Color); + $Points = []; + } + + if ($X != VOID && $LastX != null && $LastY != null) { + if (count($Points)) { + $Points[] = $YZero; + $Points[] = $LastY; + } + $Points[] = $LastX; + $Points[] = $LastY; + $Points[] = $LastX; + $Points[] = $Y; + $Points[] = $X; + $Points[] = $Y; + } + + if ($X != VOID) { + $LastGoodY = $Y; + $LastGoodX = $X; + } + if ($X == VOID) { + $X = null; + } + + if ($LastX == null && $ReCenter) { + $Y = $Y - $YStep / 2; + } + $LastX = $X; + $LastY = $Y; + if ($LastY < $this->GraphAreaY1 + $XMargin) { + $LastY = $this->GraphAreaY1 + $XMargin; + } + $Y = $Y + $YStep; + } + + if ($ReCenter) { + $Points[] = $LastX; + $Points[] = $LastY + $YStep / 2; + $Points[] = $YZero; + $Points[] = $LastY + $YStep / 2; + } else { + $Points[] = $YZero; + $Points[] = $LastY; + } + + $this->drawPolygon($Points, $Color); + } + } + } + } + + /** + * Draw an area chart + * @param array $Format + */ + public function drawAreaChart(array $Format = []) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]); + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + + if ($Threshold != null) { + foreach ($Threshold as $Key => $Params) { + $Threshold[$Key]["MinX"] = $this->scaleComputeY( + $Params["Min"], + ["AxisID" => $Serie["Axis"]] + ); + $Threshold[$Key]["MaxX"] = $this->scaleComputeY( + $Params["Max"], + ["AxisID" => $Serie["Axis"]] + ); + } + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + + $Areas = []; + $AreaID = 0; + $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin; + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaY2 - 1; + } + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $Y) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($Serie["Data"][$Key] > 0) { + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_TOPMIDDLE; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X, + $Y - $Offset, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align] + ); + } + + if ($Y == VOID && isset($Areas[$AreaID])) { + if ($LastX == null) { + $Areas[$AreaID][] = $X; + } else { + $Areas[$AreaID][] = $LastX; + } + + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaY2 - 1; + } + $AreaID++; + } elseif ($Y != VOID) { + if (!isset($Areas[$AreaID])) { + $Areas[$AreaID][] = $X; + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaY2 - 1; + } + } + + $Areas[$AreaID][] = $X; + $Areas[$AreaID][] = $Y; + } + + $LastX = $X; + $X = $X + $XStep; + } + $Areas[$AreaID][] = $LastX; + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaY2 - 1; + } + + /* Handle shadows in the areas */ + if ($this->Shadow) { + $ShadowArea = []; + foreach ($Areas as $Key => $Points) { + $ShadowArea[$Key] = []; + foreach ($Points as $Key2 => $Value) { + if ($Key2 % 2 == 0) { + $ShadowArea[$Key][] = $Value + $this->ShadowX; + } else { + $ShadowArea[$Key][] = $Value + $this->ShadowY; + } + } + } + + foreach ($ShadowArea as $Key => $Points) { + $this->drawPolygonChart( + $Points, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa + ] + ); + } + } + + $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha; + $Color = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Threshold" => $Threshold + ]; + + foreach ($Areas as $Key => $Points) { + $this->drawPolygonChart($Points, $Color); + } + } else { + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + + $Areas = []; + $AreaID = 0; + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaX1 + 1; + } + $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin; + + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + $LastX = null; + $LastY = null; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $X) { + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($Serie["Data"][$Key] > 0) { + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_TOPMIDDLE; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X + $Offset, + $Y, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "Angle" => 270, + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => $Align + ] + ); + } + + if ($X == VOID && isset($Areas[$AreaID])) { + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaX1 + 1; + } + + if ($LastY == null) { + $Areas[$AreaID][] = $Y; + } else { + $Areas[$AreaID][] = $LastY; + } + + $AreaID++; + } elseif ($X != VOID) { + if (!isset($Areas[$AreaID])) { + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaX1 + 1; + } + $Areas[$AreaID][] = $Y; + } + + $Areas[$AreaID][] = $X; + $Areas[$AreaID][] = $Y; + } + + $LastX = $X; + $LastY = $Y; + $Y = $Y + $YStep; + } + if ($AroundZero) { + $Areas[$AreaID][] = $YZero; + } else { + $Areas[$AreaID][] = $this->GraphAreaX1 + 1; + } + $Areas[$AreaID][] = $LastY; + + /* Handle shadows in the areas */ + if ($this->Shadow) { + $ShadowArea = []; + foreach ($Areas as $Key => $Points) { + $ShadowArea[$Key] = []; + foreach ($Points as $Key2 => $Value) { + if ($Key2 % 2 == 0) { + $ShadowArea[$Key][] = $Value + $this->ShadowX; + } else { + $ShadowArea[$Key][] = $Value + $this->ShadowY; + } + } + } + + foreach ($ShadowArea as $Key => $Points) { + $this->drawPolygonChart( + $Points, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa + ] + ); + } + } + + $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha; + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Threshold" => $Threshold]; + + foreach ($Areas as $Key => $Points) { + $this->drawPolygonChart($Points, $Color); + } + } + } + } + } + + /** + * Draw a bar chart + * @param array $Format + */ + public function drawBarChart(array $Format = []) + { + $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : null; + $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : null; + $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : false; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName; + $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; + $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE; + $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : true; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true; + $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; + $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : false; + $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : false; + $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; + $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; + $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6; + $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : null; + $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30; + $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null; + $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; + $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; + $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + if ($OverrideColors != null) { + $OverrideColors = $this->validatePalette($OverrideColors, $OverrideSurrounding); + $this->DataSet->saveExtendedData("Palette", $OverrideColors); + } + + $RestoreShadow = $this->Shadow; + + $SeriesCount = $this->countDrawableSeries(); + $CurrentSerie = 0; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = $R; + $DisplayG = $G; + $DisplayB = $B; + } + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + if ($InnerSurrounding != null) { + $InnerBorderR = $R + $InnerSurrounding; + $InnerBorderG = $G + $InnerSurrounding; + $InnerBorderB = $B + $InnerSurrounding; + } + if ($InnerBorderR == -1) { + $InnerColor = null; + } else { + $InnerColor = [ + "R" => $InnerBorderR, + "G" => $InnerBorderG, + "B" => $InnerBorderB + ]; + } + $Color = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ]; + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + if ($Floating0Value != null) { + $YZero = $this->scaleComputeY( + $Floating0Value, + ["AxisID" => $Serie["Axis"]] + ); + } else { + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + } + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero < $this->GraphAreaY1 + 1) { + $YZero = $this->GraphAreaY1 + 1; + } + + if ($XDivs == 0) { + $XStep = 0; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + + if ($AroundZero) { + $Y1 = $YZero; + } else { + $Y1 = $this->GraphAreaY2 - 1; + } + if ($XDivs == 0) { + $XSize = ($this->GraphAreaX2 - $this->GraphAreaX1) / ($SeriesCount + $Interleave); + } else { + $XSize = ($XStep / ($SeriesCount + $Interleave)); + } + + $XOffset = -($XSize * $SeriesCount) / 2 + $CurrentSerie * $XSize; + if ($X + $XOffset <= $this->GraphAreaX1) { + $XOffset = $this->GraphAreaX1 - $X + 1; + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2; + + if ($Rounded || $BorderR != -1) { + $XSpace = 1; + } else { + $XSpace = 0; + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $ID = 0; + foreach ($PosArray as $Key => $Y2) { + if ($Floating0Serie != null) { + if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) { + $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; + } else { + $Value = 0; + } + + $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]); + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero < $this->GraphAreaY1 + 1) { + $YZero = $this->GraphAreaY1 + 1; + } + + if ($AroundZero) { + $Y1 = $YZero; + } else { + $Y1 = $this->GraphAreaY2 - 1; + } + } + + if ($OverrideColors != null) { + if (isset($OverrideColors[$ID])) { + $Color = [ + "R" => $OverrideColors[$ID]["R"], + "G" => $OverrideColors[$ID]["G"], + "B" => $OverrideColors[$ID]["B"], + "Alpha" => $OverrideColors[$ID]["Alpha"], + "BorderR" => $OverrideColors[$ID]["BorderR"], + "BorderG" => $OverrideColors[$ID]["BorderG"], + "BorderB" => $OverrideColors[$ID]["BorderB"] + ]; + } else { + $Color = $this->getRandomColor(); + } + } + + if ($Y2 != VOID) { + $BarHeight = $Y1 - $Y2; + + if ($Serie["Data"][$Key] == 0) { + $this->drawLine( + $X + $XOffset + $XSpace, + $Y1, + $X + $XOffset + $XSize - $XSpace, + $Y1, + $Color + ); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X + $XOffset + $XSpace), + floor($Y1 - 1), + floor($X + $XOffset + $XSize - $XSpace), + floor($Y1 + 1) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } else { + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X + $XOffset + $XSpace), + floor($Y1), + floor($X + $XOffset + $XSize - $XSpace), + floor($Y2) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Rounded) { + $this->drawRoundedFilledRectangle( + $X + $XOffset + $XSpace, + $Y1, + $X + $XOffset + $XSize - $XSpace, + $Y2, + $RoundRadius, + $Color + ); + } else { + $this->drawFilledRectangle( + $X + $XOffset + $XSpace, + $Y1, + $X + $XOffset + $XSize - $XSpace, + $Y2, + $Color + ); + + if ($InnerColor != null) { + $this->drawRectangle( + $X + $XOffset + $XSpace + 1, + min($Y1, $Y2) + 1, + $X + $XOffset + $XSize - $XSpace - 1, + max($Y1, $Y2) - 1, + $InnerColor + ); + } + + if ($Gradient) { + $this->Shadow = false; + + if ($GradientMode == GRADIENT_SIMPLE) { + if ($Serie["Data"][$Key] >= 0) { + $GradienColor = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + } else { + $GradienColor = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + } + $this->drawGradientArea( + $X + $XOffset + $XSpace, + $Y1, + $X + $XOffset + $XSize - $XSpace, + $Y2, + DIRECTION_VERTICAL, + $GradienColor + ); + } elseif ($GradientMode == GRADIENT_EFFECT_CAN) { + $GradienColor1 = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + $GradienColor2 = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $XSpan = floor($XSize / 3); + + $this->drawGradientArea( + $X + $XOffset + $XSpace, + $Y1, + $X + $XOffset + $XSpan - $XSpace, + $Y2, + DIRECTION_HORIZONTAL, + $GradienColor1 + ); + $this->drawGradientArea( + $X + $XOffset + $XSpan + $XSpace, + $Y1, + $X + $XOffset + $XSize - $XSpace, + $Y2, + DIRECTION_HORIZONTAL, + $GradienColor2 + ); + } + $this->Shadow = $RestoreShadow; + } + } + + if ($Draw0Line) { + $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]; + + if (abs($Y1 - $Y2) > 3) { + $Line0Width = 3; + } else { + $Line0Width = 1; + } + if ($Y1 - $Y2 < 0) { + $Line0Width = -$Line0Width; + } + + $this->drawFilledRectangle( + $X + $XOffset + $XSpace, + floor($Y1), + $X + $XOffset + $XSize - $XSpace, + floor($Y1) - $Line0Width, + $Line0Color + ); + $this->drawLine( + $X + $XOffset + $XSpace, + floor($Y1), + $X + $XOffset + $XSize - $XSpace, + floor($Y1), + $Line0Color + ); + } + } + + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($DisplayShadow) { + $this->Shadow = true; + } + + $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit); + $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 90, $Caption); + $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin; + + if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight)) { + $CenterX = (($X + $XOffset + $XSize - $XSpace) - ($X + $XOffset + $XSpace)) + / 2 + $X + $XOffset + $XSpace + ; + $CenterY = ($Y2 - $Y1) / 2 + $Y1; + + $this->drawText( + $CenterX, + $CenterY, + $Caption, + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize, + "Angle" => 90 + ] + ); + } else { + if ($Serie["Data"][$Key] >= 0) { + $Align = TEXT_ALIGN_BOTTOMMIDDLE; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_TOPMIDDLE; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X + $XOffset + $XSize / 2, + $Y2 - $Offset, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => $Align, + "FontSize" => $DisplaySize + ] + ); + } + + $this->Shadow = $RestoreShadow; + } + } + + $X = $X + $XStep; + $ID++; + } + } else { + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + + if ($XDivs == 0) { + $YStep = 0; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + + $Y = $this->GraphAreaY1 + $XMargin; + + if ($AroundZero) { + $X1 = $YZero; + } else { + $X1 = $this->GraphAreaX1 + 1; + } + if ($XDivs == 0) { + $YSize = ($this->GraphAreaY2 - $this->GraphAreaY1) / ($SeriesCount + $Interleave); + } else { + $YSize = ($YStep / ($SeriesCount + $Interleave)); + } + + $YOffset = -($YSize * $SeriesCount) / 2 + $CurrentSerie * $YSize; + if ($Y + $YOffset <= $this->GraphAreaY1) { + $YOffset = $this->GraphAreaY1 - $Y + 1; + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2; + + if ($Rounded || $BorderR != -1) { + $YSpace = 1; + } else { + $YSpace = 0; + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $ID = 0; + foreach ($PosArray as $Key => $X2) { + if ($Floating0Serie != null) { + if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) { + $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; + } else { + $Value = 0; + } + + $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]); + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + if ($AroundZero) { + $X1 = $YZero; + } else { + $X1 = $this->GraphAreaX1 + 1; + } + } + + if ($OverrideColors != null) { + if (isset($OverrideColors[$ID])) { + $Color = [ + "R" => $OverrideColors[$ID]["R"], + "G" => $OverrideColors[$ID]["G"], + "B" => $OverrideColors[$ID]["B"], + "Alpha" => $OverrideColors[$ID]["Alpha"], + "BorderR" => $OverrideColors[$ID]["BorderR"], + "BorderG" => $OverrideColors[$ID]["BorderG"], + "BorderB" => $OverrideColors[$ID]["BorderB"] + ]; + } else { + $Color = $this->getRandomColor(); + } + } + + if ($X2 != VOID) { + $BarWidth = $X2 - $X1; + if ($Serie["Data"][$Key] == 0) { + $this->drawLine( + $X1, + $Y + $YOffset + $YSpace, + $X1, + $Y + $YOffset + $YSize - $YSpace, + $Color + ); + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X1 - 1), + floor($Y + $YOffset + $YSpace), + floor($X1 + 1), + floor($Y + $YOffset + $YSize - $YSpace) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + } else { + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X1), + floor($Y + $YOffset + $YSpace), + floor($X2), + floor($Y + $YOffset + $YSize - $YSpace) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Rounded) { + $this->drawRoundedFilledRectangle( + $X1 + 1, + $Y + $YOffset + $YSpace, + $X2, + $Y + $YOffset + $YSize - $YSpace, + $RoundRadius, + $Color + ); + } else { + $this->drawFilledRectangle( + $X1, + $Y + $YOffset + $YSpace, + $X2, + $Y + $YOffset + $YSize - $YSpace, + $Color + ); + + if ($InnerColor != null) { + $this->drawRectangle( + min($X1, $X2) + 1, + $Y + $YOffset + $YSpace + 1, + max($X1, $X2) - 1, + $Y + $YOffset + $YSize - $YSpace - 1, + $InnerColor + ); + } + + if ($Gradient) { + $this->Shadow = false; + + if ($GradientMode == GRADIENT_SIMPLE) { + if ($Serie["Data"][$Key] >= 0) { + $GradienColor = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + } else { + $GradienColor = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + } + $this->drawGradientArea( + $X1, + $Y + $YOffset + $YSpace, + $X2, + $Y + $YOffset + $YSize - $YSpace, + DIRECTION_HORIZONTAL, + $GradienColor + ); + } elseif ($GradientMode == GRADIENT_EFFECT_CAN) { + $GradienColor1 = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + $GradienColor2 = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $YSpan = floor($YSize / 3); + + $this->drawGradientArea( + $X1, + $Y + $YOffset + $YSpace, + $X2, + $Y + $YOffset + $YSpan - $YSpace, + DIRECTION_VERTICAL, + $GradienColor1 + ); + $this->drawGradientArea( + $X1, + $Y + $YOffset + $YSpan, + $X2, + $Y + $YOffset + $YSize - $YSpace, + DIRECTION_VERTICAL, + $GradienColor2 + ); + } + $this->Shadow = $RestoreShadow; + } + } + + if ($Draw0Line) { + $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]; + + if (abs($X1 - $X2) > 3) { + $Line0Width = 3; + } else { + $Line0Width = 1; + } + if ($X2 - $X1 < 0) { + $Line0Width = -$Line0Width; + } + + $this->drawFilledRectangle( + floor($X1), + $Y + $YOffset + $YSpace, + floor($X1) + $Line0Width, + $Y + $YOffset + $YSize - $YSpace, + $Line0Color + ); + $this->drawLine( + floor($X1), + $Y + $YOffset + $YSpace, + floor($X1), + $Y + $YOffset + $YSize - $YSpace, + $Line0Color + ); + } + } + + if ($DisplayValues && $Serie["Data"][$Key] != VOID) { + if ($DisplayShadow) { + $this->Shadow = true; + } + + $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit); + $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin; + + if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth)) { + $CenterX = ($X2 - $X1) / 2 + $X1; + $CenterY = (($Y + $YOffset + $YSize - $YSpace) + - ($Y + $YOffset + $YSpace)) / 2 + + ($Y + $YOffset + $YSpace) + ; + + $this->drawText( + $CenterX, + $CenterY, + $Caption, + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize + ] + ); + } else { + if ($Serie["Data"][$Key] >= 0) { + $Align = TEXT_ALIGN_MIDDLELEFT; + $Offset = $DisplayOffset; + } else { + $Align = TEXT_ALIGN_MIDDLERIGHT; + $Offset = -$DisplayOffset; + } + $this->drawText( + $X2 + $Offset, + $Y + $YOffset + $YSize / 2, + $Caption, + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => $Align, + "FontSize" => $DisplaySize + ] + ); + } + + $this->Shadow = $RestoreShadow; + } + } + $Y = $Y + $YStep; + $ID++; + } + } + $CurrentSerie++; + } + } + } + + /** + * Draw a bar chart + * @param array $Format + */ + public function drawStackedBarChart(array $Format = []) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false; + $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO; + $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName; + $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; + $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : false; + $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : false; + $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; + $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; + $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null; + $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; + $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; + $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; + $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8; + + $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + $RestoreShadow = $this->Shadow; + + $LastX = []; + $LastY = []; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($DisplayColor == DISPLAY_AUTO) { + $DisplayR = 255; + $DisplayG = 255; + $DisplayB = 255; + } + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + if ($InnerSurrounding != null) { + $InnerBorderR = $R + $InnerSurrounding; + $InnerBorderG = $G + $InnerSurrounding; + $InnerBorderB = $B + $InnerSurrounding; + } + if ($InnerBorderR == -1) { + $InnerColor = null; + } else { + $InnerColor = [ + "R" => $InnerBorderR, + "G" => $InnerBorderG, + "B" => $InnerBorderB + ]; + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { + $SerieDescription = $Serie["Description"]; + } else { + $SerieDescription = $SerieName; + } + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]], + true + ); + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + $Color = [ + "TransCorner" => true, + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "BorderR" => $BorderR, + "BorderG" => $BorderG, + "BorderB" => $BorderB + ]; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + + $XSize = ($XStep / (1 + $Interleave)); + $XOffset = -($XSize / 2); + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $Height) { + if ($Height != VOID && $Serie["Data"][$Key] != 0) { + if ($Serie["Data"][$Key] > 0) { + $Pos = "+"; + } else { + $Pos = "-"; + } + + if (!isset($LastY[$Key])) { + $LastY[$Key] = []; + } + if (!isset($LastY[$Key][$Pos])) { + $LastY[$Key][$Pos] = $YZero; + } + + $Y1 = $LastY[$Key][$Pos]; + $Y2 = $Y1 - $Height; + + if (($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero)) { + $YSpaceUp = 1; + } else { + $YSpaceUp = 0; + } + if (($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero)) { + $YSpaceDown = 1; + } else { + $YSpaceDown = 0; + } + + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X + $XOffset), + floor($Y1 - $YSpaceUp + $YSpaceDown), + floor($X + $XOffset + $XSize), + floor($Y2) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Rounded) { + $this->drawRoundedFilledRectangle( + $X + $XOffset, + $Y1 - $YSpaceUp + $YSpaceDown, + $X + $XOffset + $XSize, + $Y2, + $RoundRadius, + $Color + ); + } else { + $this->drawFilledRectangle( + $X + $XOffset, + $Y1 - $YSpaceUp + $YSpaceDown, + $X + $XOffset + $XSize, + $Y2, + $Color + ); + + if ($InnerColor != null) { + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + $this->drawRectangle( + min($X + $XOffset + 1, $X + $XOffset + $XSize), + min($Y1 - $YSpaceUp + $YSpaceDown, $Y2) + 1, + max($X + $XOffset + 1, $X + $XOffset + $XSize) - 1, + max($Y1 - $YSpaceUp + $YSpaceDown, $Y2) - 1, + $InnerColor + ); + $this->Shadow = $RestoreShadow; + } + + if ($Gradient) { + $this->Shadow = false; + + if ($GradientMode == GRADIENT_SIMPLE) { + $GradientColor = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $this->drawGradientArea( + $X + $XOffset, + $Y1 - 1 - $YSpaceUp + $YSpaceDown, + $X + $XOffset + $XSize, + $Y2 + 1, + DIRECTION_VERTICAL, + $GradientColor + ); + } elseif ($GradientMode == GRADIENT_EFFECT_CAN) { + $GradientColor1 = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + $GradientColor2 = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $XSpan = floor($XSize / 3); + + $this->drawGradientArea( + $X + $XOffset - .5, + $Y1 - .5 - $YSpaceUp + $YSpaceDown, + $X + $XOffset + $XSpan, + $Y2 + .5, + DIRECTION_HORIZONTAL, + $GradientColor1 + ); + $this->drawGradientArea( + $X + $XSpan + $XOffset - .5, + $Y1 - .5 - $YSpaceUp + $YSpaceDown, + $X + $XOffset + $XSize, + $Y2 + .5, + DIRECTION_HORIZONTAL, + $GradientColor2 + ); + } + $this->Shadow = $RestoreShadow; + } + } + + if ($DisplayValues) { + $BarHeight = abs($Y2 - $Y1) - 2; + $BarWidth = $XSize + ($XOffset / 2) - $FontFactor; + + $Caption = $this->scaleFormat( + round($Serie["Data"][$Key], $DisplayRound), + $Mode, + $Format, + $Unit + ); + $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption); + $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); + $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); + + $XCenter = (($X + $XOffset + $XSize) - ($X + $XOffset)) / 2 + $X + $XOffset; + $YCenter = (($Y2) - ($Y1 - $YSpaceUp + $YSpaceDown)) / 2 + + $Y1 - $YSpaceUp + $YSpaceDown + ; + + $Done = false; + if ($DisplayOrientation == ORIENTATION_HORIZONTAL + || $DisplayOrientation == ORIENTATION_AUTO + ) { + if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) { + $this->drawText( + $XCenter, + $YCenter, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize, + "FontName" => $DisplayFont + ] + ); + $Done = true; + } + } + + if ($DisplayOrientation == ORIENTATION_VERTICAL + || ($DisplayOrientation == ORIENTATION_AUTO && !$Done) + ) { + if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) { + $this->drawText( + $XCenter, + $YCenter, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Angle" => 90, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize, + "FontName" => $DisplayFont + ] + ); + } + } + } + $LastY[$Key][$Pos] = $Y2; + } + $X = $X + $XStep; + } + } else { + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + + $YSize = $YStep / (1 + $Interleave); + $YOffset = -($YSize / 2); + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + foreach ($PosArray as $Key => $Width) { + if ($Width != VOID && $Serie["Data"][$Key] != 0) { + if ($Serie["Data"][$Key] > 0) { + $Pos = "+"; + } else { + $Pos = "-"; + } + + if (!isset($LastX[$Key])) { + $LastX[$Key] = []; + } + if (!isset($LastX[$Key][$Pos])) { + $LastX[$Key][$Pos] = $YZero; + } + + $X1 = $LastX[$Key][$Pos]; + $X2 = $X1 + $Width; + + if (($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero)) { + $XSpaceLeft = 2; + } else { + $XSpaceLeft = 0; + } + if (($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero)) { + $XSpaceRight = 2; + } else { + $XSpaceRight = 0; + } + + if ($RecordImageMap) { + $this->addToImageMap( + "RECT", + sprintf( + "%s,%s,%s,%s", + floor($X1 + $XSpaceLeft), + floor($Y + $YOffset), + floor($X2 - $XSpaceRight), + floor($Y + $YOffset + $YSize) + ), + $this->toHTMLColor($R, $G, $B), + $SerieDescription, + $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit) + ); + } + + if ($Rounded) { + $this->drawRoundedFilledRectangle( + $X1 + $XSpaceLeft, + $Y + $YOffset, + $X2 - $XSpaceRight, + $Y + $YOffset + $YSize, + $RoundRadius, + $Color + ); + } else { + $this->drawFilledRectangle( + $X1 + $XSpaceLeft, + $Y + $YOffset, + $X2 - $XSpaceRight, + $Y + $YOffset + $YSize, + $Color + ); + + if ($InnerColor != null) { + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + $this->drawRectangle( + min($X1 + $XSpaceLeft, $X2 - $XSpaceRight) + 1, + min($Y + $YOffset, $Y + $YOffset + $YSize) + 1, + max($X1 + $XSpaceLeft, $X2 - $XSpaceRight) - 1, + max($Y + $YOffset, $Y + $YOffset + $YSize) - 1, + $InnerColor + ); + $this->Shadow = $RestoreShadow; + } + + if ($Gradient) { + $this->Shadow = false; + + if ($GradientMode == GRADIENT_SIMPLE) { + $GradientColor = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $this->drawGradientArea( + $X1 + $XSpaceLeft, + $Y + $YOffset, + $X2 - $XSpaceRight, + $Y + $YOffset + $YSize, + DIRECTION_HORIZONTAL, + $GradientColor + ); + } elseif ($GradientMode == GRADIENT_EFFECT_CAN) { + $GradientColor1 = [ + "StartR" => $GradientEndR, + "StartG" => $GradientEndG, + "StartB" => $GradientEndB, + "EndR" => $GradientStartR, + "EndG" => $GradientStartG, + "EndB" => $GradientStartB, + "Alpha" => $GradientAlpha + ]; + $GradientColor2 = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $GradientAlpha + ]; + $YSpan = floor($YSize / 3); + + $this->drawGradientArea( + $X1 + $XSpaceLeft, + $Y + $YOffset, + $X2 - $XSpaceRight, + $Y + $YOffset + $YSpan, + DIRECTION_VERTICAL, + $GradientColor1 + ); + $this->drawGradientArea( + $X1 + $XSpaceLeft, + $Y + $YOffset + $YSpan, + $X2 - $XSpaceRight, + $Y + $YOffset + $YSize, + DIRECTION_VERTICAL, + $GradientColor2 + ); + } + $this->Shadow = $RestoreShadow; + } + } + + if ($DisplayValues) { + $BarWidth = abs($X2 - $X1) - $FontFactor; + $BarHeight = $YSize + ($YOffset / 2) - $FontFactor / 2; + $Caption = $this->scaleFormat( + round($Serie["Data"][$Key], $DisplayRound), + $Mode, + $Format, + $Unit + ); + $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption); + $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); + $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); + + $XCenter = ($X2 - $X1) / 2 + $X1; + $YCenter = (($Y + $YOffset + $YSize) - ($Y + $YOffset)) / 2 + $Y + $YOffset; + + $Done = false; + if ($DisplayOrientation == ORIENTATION_HORIZONTAL + || $DisplayOrientation == ORIENTATION_AUTO + ) { + if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) { + $this->drawText( + $XCenter, + $YCenter, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize, + "FontName" => $DisplayFont + ] + ); + $Done = true; + } + } + + if ($DisplayOrientation == ORIENTATION_VERTICAL + || ($DisplayOrientation == ORIENTATION_AUTO && !$Done) + ) { + if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) { + $this->drawText( + $XCenter, + $YCenter, + $this->scaleFormat( + $Serie["Data"][$Key], + $Mode, + $Format, + $Unit + ), + [ + "R" => $DisplayR, + "G" => $DisplayG, + "B" => $DisplayB, + "Angle" => 90, + "Align" => TEXT_ALIGN_MIDDLEMIDDLE, + "FontSize" => $DisplaySize, + "FontName" => $DisplayFont + ] + ); + } + } + } + + $LastX[$Key][$Pos] = $X2; + } + + $Y = $Y + $YStep; + } + } + } + } + } + + /** + * Draw a stacked area chart + * @param array $Format + */ + public function drawStackedAreaChart(array $Format = []) + { + $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : false; + $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : null; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; + $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : false; + $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1; + $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : null; + $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0; + $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0; + $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0; + $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null; + + $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + + /* Build the offset data series */ + $OverallOffset = []; + $SerieOrder = []; + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $SerieOrder[] = $SerieName; + + foreach ($Serie["Data"] as $Key => $Value) { + if ($Value == VOID) { + $Value = 0; + } + if ($Value >= 0) { + $Sign = "+"; + } else { + $Sign = "-"; + } + if (!isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign])) { + $OverallOffset[$Key][$Sign] = 0; + } + + if ($Sign == "+") { + $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign]; + } else { + $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign]; + } + + $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value); + } + } + } + $SerieOrder = array_reverse($SerieOrder); + + foreach ($SerieOrder as $Key => $SerieName) { + $Serie = $Data["Series"][$SerieName]; + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + if ($ForceTransparency != null) { + $Alpha = $ForceTransparency; + } + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]; + if ($LineSurrounding != null) { + $LineColor = [ + "R" => $R + $LineSurrounding, + "G" => $G + $LineSurrounding, + "B" => $B + $LineSurrounding, + "Alpha" => $Alpha + ]; + } elseif ($LineR != VOID) { + $LineColor = [ + "R" => $LineR, + "G" => $LineG, + "B" => $LineB, + "Alpha" => $LineAlpha + ]; + } else { + $LineColor = $Color; + } + if ($PlotBorderSurrounding != null) { + $PlotBorderColor = [ + "R" => $R + $PlotBorderSurrounding, + "G" => $G + $PlotBorderSurrounding, + "B" => $B + $PlotBorderSurrounding, + "Alpha" => $PlotBorderAlpha + ]; + } else { + $PlotBorderColor = [ + "R" => $PlotBorderR, + "G" => $PlotBorderG, + "B" => $PlotBorderB, + "Alpha" => $PlotBorderAlpha + ]; + } + $AxisID = $Serie["Axis"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]], + true + ); + $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($YZero < $this->GraphAreaY1 + 1) { + $YZero = $this->GraphAreaY1 + 1; + } + if ($YZero > $this->GraphAreaY2 - 1) { + $YZero = $this->GraphAreaY2 - 1; + } + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $Plots = []; + $Plots[] = $X; + $Plots[] = $YZero; + foreach ($PosArray as $Key => $Height) { + if ($Height != VOID) { + $Plots[] = $X; + $Plots[] = $YZero - $Height; + } + $X = $X + $XStep; + } + $Plots[] = $X - $XStep; + $Plots[] = $YZero; + + $this->drawPolygon($Plots, $Color); + + $this->Shadow = $RestoreShadow; + if ($DrawLine) { + for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) { + $this->drawLine( + $Plots[$i], + $Plots[$i + 1], + $Plots[$i + 2], + $Plots[$i + 3], + $LineColor + ); + } + } + if ($DrawPlot) { + for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) { + if ($PlotBorder != 0) { + $this->drawFilledCircle( + $Plots[$i], + $Plots[$i + 1], + $PlotRadius + $PlotBorder, + $PlotBorderColor + ); + } + $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color); + } + } + $this->Shadow = false; + } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { + if ($YZero < $this->GraphAreaX1 + 1) { + $YZero = $this->GraphAreaX1 + 1; + } + if ($YZero > $this->GraphAreaX2 - 1) { + $YZero = $this->GraphAreaX2 - 1; + } + + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $Plots = []; + $Plots[] = $YZero; + $Plots[] = $Y; + foreach ($PosArray as $Key => $Height) { + if ($Height != VOID) { + $Plots[] = $YZero + $Height; + $Plots[] = $Y; + } + $Y = $Y + $YStep; + } + $Plots[] = $YZero; + $Plots[] = $Y - $YStep; + + $this->drawPolygon($Plots, $Color); + + $this->Shadow = $RestoreShadow; + if ($DrawLine) { + for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) { + $this->drawLine( + $Plots[$i], + $Plots[$i + 1], + $Plots[$i + 2], + $Plots[$i + 3], + $LineColor + ); + } + } + if ($DrawPlot) { + for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) { + if ($PlotBorder != 0) { + $this->drawFilledCircle( + $Plots[$i], + $Plots[$i + 1], + $PlotRadius + $PlotBorder, + $PlotBorderColor + ); + } + + $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color); + } + } + $this->Shadow = false; + } + } + } + $this->Shadow = $RestoreShadow; + } + + /** + * Draw the derivative chart associated to the data series + * @param array $Format + */ + public function drawDerivative(array $Format = []) + { + $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10; + $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3; + $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4; + $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : false; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20; + $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : true; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : true; + $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10; + $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20; + $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4; + $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : false; + $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : false; + $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0; + $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0; + $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0; + $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255; + $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255; + $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255; + $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80; + $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184; + $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234; + $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88; + $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239; + $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31; + $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36; + $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184; + $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234; + $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88; + $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67; + $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124; + $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227; + + $Data = $this->DataSet->getData(); + + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset; + } else { + $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset; + } + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + $Alpha = $Serie["Color"]["Alpha"]; + $Ticks = $Serie["Ticks"]; + $Weight = $Serie["Weight"]; + + $AxisID = $Serie["Axis"]; + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($Caption) { + if ($CaptionLine) { + $StartX = floor($this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin); + $EndX = floor($this->GraphAreaX1 - $CaptionMargin + $XMargin); + + $CaptionSettings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ]; + if ($CaptionBox) { + $this->drawFilledRectangle( + $StartX, + $YPos, + $EndX, + $YPos + $CaptionHeight, + [ + "R" => $CaptionFillR, + "G" => $CaptionFillG, + "B" => $CaptionFillB, + "BorderR" => $CaptionBorderR, + "BorderG" => $CaptionBorderG, + "BorderB" => $CaptionBorderB, + "Alpha" => $CaptionFillAlpha + ] + ); + } + $this->drawLine( + $StartX + 2, + $YPos + ($CaptionHeight / 2), + $EndX - 2, + $YPos + ($CaptionHeight / 2), + $CaptionSettings + ); + } else { + $this->drawFilledRectangle( + $this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin, + $YPos, + $this->GraphAreaX1 - $CaptionMargin + $XMargin, + $YPos + $CaptionHeight, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "BorderR" => $CaptionBorderR, + "BorderG" => $CaptionBorderG, + "BorderB" => $CaptionBorderB + ] + ); + } + } + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + + $TopY = $YPos + ($CaptionHeight / 2) - ($DerivativeHeight / 2); + $BottomY = $YPos + ($CaptionHeight / 2) + ($DerivativeHeight / 2); + + $StartX = floor($this->GraphAreaX1 + $XMargin); + $EndX = floor($this->GraphAreaX2 - $XMargin); + + if ($DrawBackground) { + $this->drawFilledRectangle( + $StartX - 1, + $TopY - 1, + $EndX + 1, + $BottomY + 1, + [ + "R" => $BackgroundR, + "G" => $BackgroundG, + "B" => $BackgroundB, + "Alpha" => $BackgroundAlpha + ] + ); + } + if ($DrawBorder) { + $this->drawRectangle( + $StartX - 1, + $TopY - 1, + $EndX + 1, + $BottomY + 1, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha + ] + ); + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + + /* Determine the Max slope index */ + $LastX = null; + $LastY = null; + $MinSlope = 0; + $MaxSlope = 1; + foreach ($PosArray as $Key => $Y) { + if ($Y != VOID && $LastX != null) { + $Slope = ($LastY - $Y); + if ($Slope > $MaxSlope) { + $MaxSlope = $Slope; + } if ($Slope < $MinSlope) { + $MinSlope = $Slope; + } + } + + if ($Y == VOID) { + $LastX = null; + $LastY = null; + } else { + $LastX = $X; + $LastY = $Y; + } + } + + $LastX = null; + $LastY = null; + $LastColor = null; + foreach ($PosArray as $Key => $Y) { + if ($Y != VOID && $LastY != null) { + $Slope = ($LastY - $Y); + + if ($Slope >= 0) { + $SlopeIndex = (100 / $MaxSlope) * $Slope; + $R = (($PositiveSlopeEndR - $PositiveSlopeStartR) / 100) + * $SlopeIndex + $PositiveSlopeStartR + ; + $G = (($PositiveSlopeEndG - $PositiveSlopeStartG) / 100) + * $SlopeIndex + $PositiveSlopeStartG + ; + $B = (($PositiveSlopeEndB - $PositiveSlopeStartB) / 100) + * $SlopeIndex + $PositiveSlopeStartB + ; + } elseif ($Slope < 0) { + $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); + $R = (($NegativeSlopeEndR - $NegativeSlopeStartR) / 100) + * $SlopeIndex + $NegativeSlopeStartR + ; + $G = (($NegativeSlopeEndG - $NegativeSlopeStartG) / 100) + * $SlopeIndex + $NegativeSlopeStartG + ; + $B = (($NegativeSlopeEndB - $NegativeSlopeStartB) / 100) + * $SlopeIndex + $NegativeSlopeStartB + ; + } + + $Color = ["R" => $R, "G" => $G, "B" => $B]; + + if ($ShadedSlopeBox && $LastColor != null) {// && $Slope != 0 + $GradientSettings = [ + "StartR" => $LastColor["R"], + "StartG" => $LastColor["G"], + "StartB" => $LastColor["B"], + "EndR" => $R, + "EndG" => $G, + "EndB" => $B + ]; + $this->drawGradientArea( + $LastX, + $TopY, + $X, + $BottomY, + DIRECTION_HORIZONTAL, + $GradientSettings + ); + } elseif (!$ShadedSlopeBox || $LastColor == null) { // || $Slope == 0 + $this->drawFilledRectangle( + floor($LastX), + $TopY, + floor($X), + $BottomY, + $Color + ); + } + $LastColor = $Color; + } + + if ($Y == VOID) { + $LastY = null; + } else { + $LastX = $X; + $LastY = $Y; + } + + $X = $X + $XStep; + } + + $YPos = $YPos + $CaptionHeight + $SerieSpacing; + } else { + if ($Caption) { + $StartY = floor($this->GraphAreaY1 - $CaptionWidth + $XMargin - $CaptionMargin); + $EndY = floor($this->GraphAreaY1 - $CaptionMargin + $XMargin); + if ($CaptionLine) { + $CaptionSettings = [ + "R" => $R, + "G" => $G, + "B" => $B, + "Alpha" => $Alpha, + "Ticks" => $Ticks, + "Weight" => $Weight + ]; + if ($CaptionBox) { + $this->drawFilledRectangle( + $XPos, + $StartY, + $XPos + $CaptionHeight, + $EndY, + [ + "R" => $CaptionFillR, + "G" => $CaptionFillG, + "B" => $CaptionFillB, + "BorderR" => $CaptionBorderR, + "BorderG" => $CaptionBorderG, + "BorderB" => $CaptionBorderB, + "Alpha" => $CaptionFillAlpha + ] + ); + } + $this->drawLine( + $XPos + ($CaptionHeight / 2), + $StartY + 2, + $XPos + ($CaptionHeight / 2), + $EndY - 2, + $CaptionSettings + ); + } else { + $this->drawFilledRectangle( + $XPos, + $StartY, + $XPos + $CaptionHeight, + $EndY, + [ + "R" => $R, + "G" => $G, + "B" => $B, + "BorderR" => $CaptionBorderR, + "BorderG" => $CaptionBorderG, + "BorderB" => $CaptionBorderB + ] + ); + } + } + + + if ($XDivs == 0) { + $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + + $TopX = $XPos + ($CaptionHeight / 2) - ($DerivativeHeight / 2); + $BottomX = $XPos + ($CaptionHeight / 2) + ($DerivativeHeight / 2); + + $StartY = floor($this->GraphAreaY1 + $XMargin); + $EndY = floor($this->GraphAreaY2 - $XMargin); + + if ($DrawBackground) { + $this->drawFilledRectangle( + $TopX - 1, + $StartY - 1, + $BottomX + 1, + $EndY + 1, + [ + "R" => $BackgroundR, + "G" => $BackgroundG, + "B" => $BackgroundB, + "Alpha" => $BackgroundAlpha + ] + ); + } + if ($DrawBorder) { + $this->drawRectangle( + $TopX - 1, + $StartY - 1, + $BottomX + 1, + $EndY + 1, + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha + ] + ); + } + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + + /* Determine the Max slope index */ + $LastX = null; + $LastY = null; + $MinSlope = 0; + $MaxSlope = 1; + foreach ($PosArray as $Key => $X) { + if ($X != VOID && $LastX != null) { + $Slope = ($X - $LastX); + if ($Slope > $MaxSlope) { + $MaxSlope = $Slope; + } + if ($Slope < $MinSlope) { + $MinSlope = $Slope; + } + } + + if ($X == VOID) { + $LastX = null; + } else { + $LastX = $X; + } + } + + $LastX = null; + $LastY = null; + $LastColor = null; + foreach ($PosArray as $Key => $X) { + if ($X != VOID && $LastX != null) { + $Slope = ($X - $LastX); + + if ($Slope >= 0) { + $SlopeIndex = (100 / $MaxSlope) * $Slope; + $R = (($PositiveSlopeEndR - $PositiveSlopeStartR) / 100) + * $SlopeIndex + $PositiveSlopeStartR + ; + $G = (($PositiveSlopeEndG - $PositiveSlopeStartG) / 100) + * $SlopeIndex + $PositiveSlopeStartG + ; + $B = (($PositiveSlopeEndB - $PositiveSlopeStartB) / 100) + * $SlopeIndex + $PositiveSlopeStartB + ; + } elseif ($Slope < 0) { + $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); + $R = (($NegativeSlopeEndR - $NegativeSlopeStartR) / 100) + * $SlopeIndex + $NegativeSlopeStartR + ; + $G = (($NegativeSlopeEndG - $NegativeSlopeStartG) / 100) + * $SlopeIndex + $NegativeSlopeStartG + ; + $B = (($NegativeSlopeEndB - $NegativeSlopeStartB) / 100) + * $SlopeIndex + $NegativeSlopeStartB + ; + } + + $Color = ["R" => $R, "G" => $G, "B" => $B]; + + if ($ShadedSlopeBox && $LastColor != null) { + $GradientSettings = [ + "StartR" => $LastColor["R"], + "StartG" => $LastColor["G"], + "StartB" => $LastColor["B"], + "EndR" => $R, + "EndG" => $G, + "EndB" => $B + ]; + + $this->drawGradientArea( + $TopX, + $LastY, + $BottomX, + $Y, + DIRECTION_VERTICAL, + $GradientSettings + ); + } elseif (!$ShadedSlopeBox || $LastColor == null) { + $this->drawFilledRectangle( + $TopX, + floor($LastY), + $BottomX, + floor($Y), + $Color + ); + } + $LastColor = $Color; + } + + if ($X == VOID) { + $LastX = null; + } else { + $LastX = $X; + $LastY = $Y; + } + + $Y = $Y + $XStep; + } + + $XPos = $XPos + $CaptionHeight + $SerieSpacing; + } + $this->Shadow = $RestoreShadow; + } + } + } + + /** + * Draw the line of best fit + * @param array $Format + */ + public function drawBestFit(array $Format = []) + { + $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : null; + $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID; + $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID; + $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID; + $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID; + + $Data = $this->DataSet->getData(); + list($XMargin, $XDivs) = $this->scaleGetXSettings(); + + foreach ($Data["Series"] as $SerieName => $Serie) { + if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { + if ($OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID) { + $R = $OverrideR; + $G = $OverrideG; + $B = $OverrideB; + } else { + $R = $Serie["Color"]["R"]; + $G = $Serie["Color"]["G"]; + $B = $Serie["Color"]["B"]; + } + if ($OverrideTicks == null) { + $Ticks = $Serie["Ticks"]; + } else { + $Ticks = $OverrideTicks; + } + if ($OverrideAlpha == VOID) { + $Alpha = $Serie["Color"]["Alpha"]; + } else { + $Alpha = $OverrideAlpha; + } + + $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]; + + $PosArray = $this->scaleComputeY( + $Serie["Data"], + ["AxisID" => $Serie["Axis"]] + ); + + if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { + if ($XDivs == 0) { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; + } else { + $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; + } + $X = $this->GraphAreaX1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $Sxy = 0; + $Sx = 0; + $Sy = 0; + $Sxx = 0; + foreach ($PosArray as $Key => $Y) { + if ($Y != VOID) { + $Sxy = $Sxy + $X * $Y; + $Sx = $Sx + $X; + $Sy = $Sy + $Y; + $Sxx = $Sxx + $X * $X; + } + + $X = $X + $XStep; + } + $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); + $M = (($n * $Sxy) - ($Sx * $Sy)) / (($n * $Sxx) - ($Sx * $Sx)); + $B = (($Sy) - ($M * $Sx)) / ($n); + + $X1 = $this->GraphAreaX1 + $XMargin; + $Y1 = $M * $X1 + $B; + $X2 = $this->GraphAreaX2 - $XMargin; + $Y2 = $M * $X2 + $B; + + if ($Y1 < $this->GraphAreaY1) { + $X1 = $X1 + ($this->GraphAreaY1 - $Y1); + $Y1 = $this->GraphAreaY1; + } + if ($Y1 > $this->GraphAreaY2) { + $X1 = $X1 + ($Y1 - $this->GraphAreaY2); + $Y1 = $this->GraphAreaY2; + } + if ($Y2 < $this->GraphAreaY1) { + $X2 = $X2 - ($this->GraphAreaY1 - $Y2); + $Y2 = $this->GraphAreaY1; + } + if ($Y2 > $this->GraphAreaY2) { + $X2 = $X2 - ($Y2 - $this->GraphAreaY2); + $Y2 = $this->GraphAreaY2; + } + + $this->drawLine($X1, $Y1, $X2, $Y2, $Color); + } else { + if ($XDivs == 0) { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; + } else { + $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; + } + $Y = $this->GraphAreaY1 + $XMargin; + + if (!is_array($PosArray)) { + $Value = $PosArray; + $PosArray = []; + $PosArray[0] = $Value; + } + $Sxy = 0; + $Sx = 0; + $Sy = 0; + $Sxx = 0; + foreach ($PosArray as $Key => $X) { + if ($X != VOID) { + $Sxy = $Sxy + $X * $Y; + $Sx = $Sx + $Y; + $Sy = $Sy + $X; + $Sxx = $Sxx + $Y * $Y; + } + + $Y = $Y + $YStep; + } + $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); + $M = (($n * $Sxy) - ($Sx * $Sy)) / (($n * $Sxx) - ($Sx * $Sx)); + $B = (($Sy) - ($M * $Sx)) / ($n); + + $Y1 = $this->GraphAreaY1 + $XMargin; + $X1 = $M * $Y1 + $B; + $Y2 = $this->GraphAreaY2 - $XMargin; + $X2 = $M * $Y2 + $B; + + if ($X1 < $this->GraphAreaX1) { + $Y1 = $Y1 + ($this->GraphAreaX1 - $X1); + $X1 = $this->GraphAreaX1; + } + if ($X1 > $this->GraphAreaX2) { + $Y1 = $Y1 + ($X1 - $this->GraphAreaX2); + $X1 = $this->GraphAreaX2; + } + if ($X2 < $this->GraphAreaX1) { + $Y2 = $Y2 - ($this->GraphAreaY1 - $X2); + $X2 = $this->GraphAreaX1; + } + if ($X2 > $this->GraphAreaX2) { + $Y2 = $Y2 - ($X2 - $this->GraphAreaX2); + $X2 = $this->GraphAreaX2; + } + + $this->drawLine($X1, $Y1, $X2, $Y2, $Color); + } + } + } + } + + /** + * Draw a label box + * @param int $X + * @param int $Y + * @param string $Title + * @param array $Captions + * @param array $Format + */ + public function drawLabelBox($X, $Y, $Title, array $Captions, array $Format = []) + { + $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : null; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50; + $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : true; + $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6; + $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4; + $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10; + $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8; + $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; + $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND; + $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R; + $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G; + $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B; + $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0; + $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0; + $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 100; + + if (!$DrawSerieColor) { + $SerieBoxSize = 0; + $SerieBoxSpacing = 0; + } + + $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, 0, $Title); + $TitleWidth = ($TxtPos[1]["X"] - $TxtPos[0]["X"]) + $VerticalMargin * 2; + $TitleHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); + + if ($NoTitle) { + $TitleWidth = 0; + $TitleHeight = 0; + } + + $CaptionWidth = 0; + $CaptionHeight = -$HorizontalMargin; + foreach ($Captions as $Key => $Caption) { + $TxtPos = $this->getTextBox( + $X, + $Y, + $FontName, + $FontSize, + 0, + $Caption["Caption"] + ); + $CaptionWidth = max( + $CaptionWidth, + ($TxtPos[1]["X"] - $TxtPos[0]["X"]) + $VerticalMargin * 2 + ); + $CaptionHeight = $CaptionHeight + + max(($TxtPos[0]["Y"] - $TxtPos[2]["Y"]), ($SerieBoxSize + 2)) + + $HorizontalMargin + ; + } + + if ($CaptionHeight <= 5) { + $CaptionHeight = $CaptionHeight + $HorizontalMargin / 2; + } + + if ($DrawSerieColor) { + $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing; + } + + $BoxWidth = max($BoxWidth, $TitleWidth, $CaptionWidth); + + $XMin = $X - 5 - floor(($BoxWidth - 10) / 2); + $XMax = $X + 5 + floor(($BoxWidth - 10) / 2); + + $RestoreShadow = $this->Shadow; + if ($this->Shadow == true) { + $this->Shadow = false; + + $Poly = []; + $Poly[] = $X + $this->ShadowX; + $Poly[] = $Y + $this->ShadowX; + $Poly[] = $X + 5 + $this->ShadowX; + $Poly[] = $Y - 5 + $this->ShadowX; + $Poly[] = $XMax + $this->ShadowX; + $Poly[] = $Y - 5 + $this->ShadowX; + + if ($NoTitle) { + $Poly[] = $XMax + $this->ShadowX; + $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX; + $Poly[] = $XMin + $this->ShadowX; + $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX; + } else { + $Poly[] = $XMax + $this->ShadowX; + $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX; + $Poly[] = $XMin + $this->ShadowX; + $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX; + } + + $Poly[] = $XMin + $this->ShadowX; + $Poly[] = $Y - 5 + $this->ShadowX; + $Poly[] = $X - 5 + $this->ShadowX; + $Poly[] = $Y - 5 + $this->ShadowX; + $this->drawPolygon( + $Poly, + [ + "R" => $this->ShadowR, + "G" => $this->ShadowG, + "B" => $this->ShadowB, + "Alpha" => $this->Shadowa + ] + ); + } + + /* Draw the background */ + $GradientSettings = [ + "StartR" => $GradientStartR, + "StartG" => $GradientStartG, + "StartB" => $GradientStartB, + "EndR" => $GradientEndR, + "EndG" => $GradientEndG, + "EndB" => $GradientEndB, + "Alpha" => $BoxAlpha + ]; + if ($NoTitle) { + $this->drawGradientArea( + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMax, + $Y - 6, + DIRECTION_VERTICAL, + $GradientSettings + ); + } else { + $this->drawGradientArea( + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax, + $Y - 6, + DIRECTION_VERTICAL, + $GradientSettings + ); + } + $Poly = []; + $Poly[] = $X; + $Poly[] = $Y; + $Poly[] = $X - 5; + $Poly[] = $Y - 5; + $Poly[] = $X + 5; + $Poly[] = $Y - 5; + $this->drawPolygon( + $Poly, + [ + "R" => $GradientEndR, + "G" => $GradientEndG, + "B" => $GradientEndB, + "Alpha" => $BoxAlpha, + "NoBorder" => true + ] + ); + + /* Outer border */ + $OuterBorderColor = $this->allocateColor($this->Picture, 100, 100, 100, $BoxAlpha); + imageline($this->Picture, $XMin, $Y - 5, $X - 5, $Y - 5, $OuterBorderColor); + imageline($this->Picture, $X, $Y, $X - 5, $Y - 5, $OuterBorderColor); + imageline($this->Picture, $X, $Y, $X + 5, $Y - 5, $OuterBorderColor); + imageline($this->Picture, $X + 5, $Y - 5, $XMax, $Y - 5, $OuterBorderColor); + if ($NoTitle) { + imageline( + $this->Picture, + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMin, + $Y - 5, + $OuterBorderColor + ); + imageline( + $this->Picture, + $XMax, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMax, + $Y - 5, + $OuterBorderColor + ); + imageline( + $this->Picture, + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMax, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $OuterBorderColor + ); + } else { + imageline( + $this->Picture, + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMin, + $Y - 5, + $OuterBorderColor + ); + imageline( + $this->Picture, + $XMax, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax, + $Y - 5, + $OuterBorderColor + ); + imageline( + $this->Picture, + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $OuterBorderColor + ); + } + + /* Inner border */ + $InnerBorderColor = $this->allocateColor($this->Picture, 255, 255, 255, $BoxAlpha); + imageline($this->Picture, $XMin + 1, $Y - 6, $X - 5, $Y - 6, $InnerBorderColor); + imageline($this->Picture, $X, $Y - 1, $X - 5, $Y - 6, $InnerBorderColor); + imageline($this->Picture, $X, $Y - 1, $X + 5, $Y - 6, $InnerBorderColor); + imageline($this->Picture, $X + 5, $Y - 6, $XMax - 1, $Y - 6, $InnerBorderColor); + if ($NoTitle) { + imageline( + $this->Picture, + $XMin + 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMin + 1, + $Y - 6, + $InnerBorderColor + ); + imageline( + $this->Picture, + $XMax - 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMax - 1, + $Y - 6, + $InnerBorderColor + ); + imageline( + $this->Picture, + $XMin + 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $XMax - 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, + $InnerBorderColor + ); + } else { + imageline( + $this->Picture, + $XMin + 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMin + 1, + $Y - 6, + $InnerBorderColor + ); + imageline( + $this->Picture, + $XMax - 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax - 1, + $Y - 6, + $InnerBorderColor + ); + imageline( + $this->Picture, + $XMin + 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax - 1, + $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $InnerBorderColor + ); + } + + /* Draw the separator line */ + if ($TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle) { + $YPos = $Y - 7 - $CaptionHeight - $HorizontalMargin - $HorizontalMargin / 2; + $XMargin = $VerticalMargin / 2; + $this->drawLine( + $XMin + $XMargin, + $YPos + 1, + $XMax - $XMargin, + $YPos + 1, + [ + "R" => $GradientEndR, + "G" => $GradientEndG, + "B" => $GradientEndB, + "Alpha" => $BoxAlpha + ] + ); + $this->drawLine( + $XMin + $XMargin, + $YPos, + $XMax - $XMargin, + $YPos, + [ + "R" => $GradientStartR, + "G" => $GradientStartG, + "B" => $GradientStartB, + "Alpha" => $BoxAlpha + ] + ); + } elseif ($TitleMode == LABEL_TITLE_BACKGROUND) { + $this->drawFilledRectangle( + $XMin, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, + $XMax, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2, + [ + "R" => $TitleBackgroundR, + "G" => $TitleBackgroundG, + "B" => $TitleBackgroundB, + "Alpha" => $BoxAlpha + ] + ); + imageline( + $this->Picture, + $XMin + 1, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1, + $XMax - 1, + $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1, + $InnerBorderColor + ); + } + + /* Write the description */ + if (!$NoTitle) { + $this->drawText( + $XMin + $VerticalMargin, + $Y - 7 - $CaptionHeight - $HorizontalMargin * 2, + $Title, + [ + "Align" => TEXT_ALIGN_BOTTOMLEFT, + "R" => $TitleR, + "G" => $TitleG, + "B" => $TitleB + ] + ); + } + + /* Write the value */ + $YPos = $Y - 5 - $HorizontalMargin; + $XPos = $XMin + $VerticalMargin + $SerieBoxSize + $SerieBoxSpacing; + foreach ($Captions as $Key => $Caption) { + $CaptionTxt = $Caption["Caption"]; + $TxtPos = $this->getTextBox($XPos, $YPos, $FontName, $FontSize, 0, $CaptionTxt); + $CaptionHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); + + /* Write the serie color if needed */ + if ($DrawSerieColor) { + $BoxSettings = [ + "R" => $Caption["Format"]["R"], + "G" => $Caption["Format"]["G"], + "B" => $Caption["Format"]["B"], + "Alpha" => $Caption["Format"]["Alpha"], + "BorderR" => 0, + "BorderG" => 0, + "BorderB" => 0 + ]; + $this->drawFilledRectangle( + $XMin + $VerticalMargin, + $YPos - $SerieBoxSize, + $XMin + $VerticalMargin + $SerieBoxSize, + $YPos, + $BoxSettings + ); + } + + $this->drawText($XPos, $YPos, $CaptionTxt, ["Align" => TEXT_ALIGN_BOTTOMLEFT]); + + $YPos = $YPos - $CaptionHeight - $HorizontalMargin; + } + + $this->Shadow = $RestoreShadow; + } + + /** + * Draw a basic shape + * @param int $X + * @param int $Y + * @param int $Shape + * @param int $PlotSize + * @param int $PlotBorder + * @param int $BorderSize + * @param int $R + * @param int $G + * @param int $B + * @param int|float $Alpha + * @param int $BorderR + * @param int $BorderG + * @param int $BorderB + * @param int|float $BorderAlpha + */ + public function drawShape( + $X, + $Y, + $Shape, + $PlotSize, + $PlotBorder, + $BorderSize, + $R, + $G, + $B, + $Alpha, + $BorderR, + $BorderG, + $BorderB, + $BorderAlpha + ) { + if ($Shape == SERIE_SHAPE_FILLEDCIRCLE) { + if ($PlotBorder) { + $this->drawFilledCircle( + $X, + $Y, + $PlotSize + $BorderSize, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha] + ); + } + $this->drawFilledCircle( + $X, + $Y, + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + } elseif ($Shape == SERIE_SHAPE_FILLEDSQUARE) { + if ($PlotBorder) { + $this->drawFilledRectangle( + $X - $PlotSize - $BorderSize, + $Y - $PlotSize - $BorderSize, + $X + $PlotSize + $BorderSize, + $Y + $PlotSize + $BorderSize, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha] + ); + } + $this->drawFilledRectangle( + $X - $PlotSize, + $Y - $PlotSize, + $X + $PlotSize, + $Y + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + } elseif ($Shape == SERIE_SHAPE_FILLEDTRIANGLE) { + if ($PlotBorder) { + $Pos = []; + $Pos[] = $X; + $Pos[] = $Y - $PlotSize - $BorderSize; + $Pos[] = $X - $PlotSize - $BorderSize; + $Pos[] = $Y + $PlotSize + $BorderSize; + $Pos[] = $X + $PlotSize + $BorderSize; + $Pos[] = $Y + $PlotSize + $BorderSize; + $this->drawPolygon( + $Pos, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha] + ); + } + + $Pos = []; + $Pos[] = $X; + $Pos[] = $Y - $PlotSize; + $Pos[] = $X - $PlotSize; + $Pos[] = $Y + $PlotSize; + $Pos[] = $X + $PlotSize; + $Pos[] = $Y + $PlotSize; + $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + } elseif ($Shape == SERIE_SHAPE_TRIANGLE) { + $this->drawLine( + $X, + $Y - $PlotSize, + $X - $PlotSize, + $Y + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + $this->drawLine( + $X - $PlotSize, + $Y + $PlotSize, + $X + $PlotSize, + $Y + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + $this->drawLine( + $X + $PlotSize, + $Y + $PlotSize, + $X, + $Y - $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + } elseif ($Shape == SERIE_SHAPE_SQUARE) { + $this->drawRectangle( + $X - $PlotSize, + $Y - $PlotSize, + $X + $PlotSize, + $Y + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + } elseif ($Shape == SERIE_SHAPE_CIRCLE) { + $this->drawCircle( + $X, + $Y, + $PlotSize, + $PlotSize, + ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha] + ); + } elseif ($Shape == SERIE_SHAPE_DIAMOND) { + $Pos = []; + $Pos[] = $X - $PlotSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y - $PlotSize; + $Pos[] = $X + $PlotSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y + $PlotSize; + $this->drawPolygon( + $Pos, + [ + "NoFill" => true, + "BorderR" => $R, + "BorderG" => $G, + "BorderB" => $B, + "BorderAlpha" => $Alpha + ] + ); + } elseif ($Shape == SERIE_SHAPE_FILLEDDIAMOND) { + if ($PlotBorder) { + $Pos = []; + $Pos[] = $X - $PlotSize - $BorderSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y - $PlotSize - $BorderSize; + $Pos[] = $X + $PlotSize + $BorderSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y + $PlotSize + $BorderSize; + $this->drawPolygon( + $Pos, + ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha] + ); + } + + $Pos = []; + $Pos[] = $X - $PlotSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y - $PlotSize; + $Pos[] = $X + $PlotSize; + $Pos[] = $Y; + $Pos[] = $X; + $Pos[] = $Y + $PlotSize; + $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]); + } + } + + /** + * + * @param array $Points + * @param array $Format + * @return null|integer + */ + public function drawPolygonChart(array $Points, array $Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : false; + $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : false; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null; + + if ($Surrounding != null) { + $BorderR = $R + $Surrounding; + $BorderG = $G + $Surrounding; + $BorderB = $B + $Surrounding; + } + + $RestoreShadow = $this->Shadow; + $this->Shadow = false; + + $AllIntegers = true; + for ($i = 0; $i <= count($Points) - 2; $i = $i + 2) { + if ($this->getFirstDecimal($Points[$i + 1]) != 0) { + $AllIntegers = false; + } + } + + /* Convert polygon to segments */ + $Segments = []; + for ($i = 2; $i <= count($Points) - 2; $i = $i + 2) { + $Segments[] = [ + "X1" => $Points[$i - 2], + "Y1" => $Points[$i - 1], + "X2" => $Points[$i], + "Y2" => $Points[$i + 1] + ]; + } + $Segments[] = [ + "X1" => $Points[$i - 2], + "Y1" => $Points[$i - 1], + "X2" => $Points[0], + "Y2" => $Points[1] + ]; + + /* Simplify straight lines */ + $Result = []; + $inHorizon = false; + $LastX = VOID; + foreach ($Segments as $Key => $Pos) { + if ($Pos["Y1"] != $Pos["Y2"]) { + if ($inHorizon) { + $inHorizon = false; + $Result[] = [ + "X1" => $LastX, + "Y1" => $Pos["Y1"], + "X2" => $Pos["X1"], + "Y2" => $Pos["Y1"] + ]; + } + + $Result[] = [ + "X1" => $Pos["X1"], + "Y1" => $Pos["Y1"], + "X2" => $Pos["X2"], + "Y2" => $Pos["Y2"] + ]; + } else { + if (!$inHorizon) { + $inHorizon = true; + $LastX = $Pos["X1"]; + } + } + } + $Segments = $Result; + + /* Do we have something to draw */ + if (!count($Segments)) { + return 0; + } + + /* For segments debugging purpose */ + //foreach($Segments as $Key => $Pos) + // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n"; + + /* Find out the min & max Y boundaries */ + $MinY = OUT_OF_SIGHT; + $MaxY = OUT_OF_SIGHT; + foreach ($Segments as $Key => $Coords) { + if ($MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"], $Coords["Y2"])) { + $MinY = min($Coords["Y1"], $Coords["Y2"]); + } + if ($MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"], $Coords["Y2"])) { + $MaxY = max($Coords["Y1"], $Coords["Y2"]); + } + } + + if ($AllIntegers) { + $YStep = 1; + } else { + $YStep = .5; + } + + $MinY = floor($MinY); + $MaxY = floor($MaxY); + + /* Scan each Y lines */ + $DefaultColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha); + $DebugLine = 0; + $DebugColor = $this->allocateColor($this->Picture, 255, 0, 0, 100); + + $MinY = floor($MinY); + $MaxY = floor($MaxY); + $YStep = 1; + + if (!$NoFill) { + //if ($DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; } + for ($Y = $MinY; $Y <= $MaxY; $Y = $Y + $YStep) { + $Intersections = []; + $LastSlope = null; + $RestoreLast = "-"; + foreach ($Segments as $Key => $Coords) { + $X1 = $Coords["X1"]; + $X2 = $Coords["X2"]; + $Y1 = $Coords["Y1"]; + $Y2 = $Coords["Y2"]; + + if (min($Y1, $Y2) <= $Y && max($Y1, $Y2) >= $Y) { + if ($Y1 == $Y2) { + $X = $X1; + } else { + $X = $X1 + (($Y - $Y1) * $X2 - ($Y - $Y1) * $X1) / ($Y2 - $Y1); + } + + $X = floor($X); + + if ($X2 == $X1) { + $Slope = "!"; + } else { + $SlopeC = ($Y2 - $Y1) / ($X2 - $X1); + if ($SlopeC == 0) { + $Slope = "="; + } elseif ($SlopeC > 0) { + $Slope = "+"; + } elseif ($SlopeC < 0) { + $Slope = "-"; + } + } + + if (!is_array($Intersections)) { + $Intersections[] = $X; + } elseif (!in_array($X, $Intersections)) { + $Intersections[] = $X; + } elseif (in_array($X, $Intersections)) { + if ($Y == $DebugLine) { + echo $Slope . "/" . $LastSlope . "(" . $X . ") "; + } + + if ($Slope == "=" && $LastSlope == "-") { + $Intersections[] = $X; + } + if ($Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=") { + $Intersections[] = $X; + } + if ($Slope != $LastSlope && $LastSlope == "!" && $Slope == "+") { + $Intersections[] = $X; + } + } + + if (is_array($Intersections) + && in_array($X, $Intersections) + && $LastSlope == "=" + && ($Slope == "-") + ) { + $Intersections[] = $X; + } + + $LastSlope = $Slope; + } + } + if ($RestoreLast != "-") { + $Intersections[] = $RestoreLast; + echo "@" . $Y . "\r\n"; + } + + if (is_array($Intersections)) { + sort($Intersections); + + if ($Y == $DebugLine) { + print_r($Intersections); + } + + /* Remove null plots */ + $Result = []; + for ($i = 0; $i <= count($Intersections) - 1; $i = $i + 2) { + if (isset($Intersections[$i + 1])) { + if ($Intersections[$i] != $Intersections[$i + 1]) { + $Result[] = $Intersections[$i]; + $Result[] = $Intersections[$i + 1]; + } + } + } + + if (is_array($Result)) { + $Intersections = $Result; + + $LastX = OUT_OF_SIGHT; + foreach ($Intersections as $Key => $X) { + if ($LastX == OUT_OF_SIGHT) { + $LastX = $X; + } elseif ($LastX != OUT_OF_SIGHT) { + if ($this->getFirstDecimal($LastX) > 1) { + $LastX++; + } + + $Color = $DefaultColor; + if ($Threshold != null) { + foreach ($Threshold as $Key => $Parameters) { + if ($Y <= $Parameters["MinX"] + && $Y >= $Parameters["MaxX"] + ) { + if (isset($Parameters["R"])) { + $R = $Parameters["R"]; + } else { + $R = 0; + } + if (isset($Parameters["G"])) { + $G = $Parameters["G"]; + } else { + $G = 0; + } + if (isset($Parameters["B"])) { + $B = $Parameters["B"]; + } else { + $B = 0; + } + if (isset($Parameters["Alpha"])) { + $Alpha = $Parameters["Alpha"]; + } else { + $Alpha = 100; + } + $Color = $this->allocateColor( + $this->Picture, + $R, + $G, + $B, + $Alpha + ); + } + } + } + + imageline($this->Picture, $LastX, $Y, $X, $Y, $Color); + + if ($Y == $DebugLine) { + imageline($this->Picture, $LastX, $Y, $X, $Y, $DebugColor); + } + + $LastX = OUT_OF_SIGHT; + } + } + } + } + } + } + + /* Draw the polygon border, if required */ + if (!$NoBorder) { + foreach ($Segments as $Key => $Coords) { + $this->drawLine( + $Coords["X1"], + $Coords["Y1"], + $Coords["X2"], + $Coords["Y2"], + [ + "R" => $BorderR, + "G" => $BorderG, + "B" => $BorderB, + "Alpha" => $BorderAlpha, + "Threshold" => $Threshold + ] + ); + } + } + + $this->Shadow = $RestoreShadow; + } +} diff --git a/vendor/szymach/c-pchart/src/Image.php b/vendor/szymach/c-pchart/src/Image.php new file mode 100644 index 0000000..360e682 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Image.php @@ -0,0 +1,748 @@ +TransparentBackground = $TransparentBackground; + + $this->DataSet = null !== $DataSet ? $DataSet : new Data(); + $this->XSize = $XSize; + $this->YSize = $YSize; + $this->Picture = imagecreatetruecolor($XSize, $YSize); + + if ($this->TransparentBackground) { + imagealphablending($this->Picture, false); + imagefilledrectangle( + $this->Picture, + 0, + 0, + $XSize, + $YSize, + imagecolorallocatealpha($this->Picture, 255, 255, 255, 127) + ); + imagealphablending($this->Picture, true); + imagesavealpha($this->Picture, true); + } else { + $C_White = $this->AllocateColor($this->Picture, 255, 255, 255); + imagefilledrectangle($this->Picture, 0, 0, $XSize, $YSize, $C_White); + } + } + + /** + * Enable / Disable and set shadow properties + * @param boolean $Enabled + * @param array $Format + */ + public function setShadow($Enabled = true, array $Format = []) + { + $X = isset($Format["X"]) ? $Format["X"] : 2; + $Y = isset($Format["Y"]) ? $Format["Y"] : 2; + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 10; + + $this->Shadow = $Enabled; + $this->ShadowX = $X; + $this->ShadowY = $Y; + $this->ShadowR = $R; + $this->ShadowG = $G; + $this->ShadowB = $B; + $this->Shadowa = $Alpha; + } + + /** + * Set the graph area position + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @return int|null + */ + public function setGraphArea($X1, $Y1, $X2, $Y2) + { + if ($X2 < $X1 || $X1 == $X2 || $Y2 < $Y1 || $Y1 == $Y2) { + return -1; + } + + $this->GraphAreaX1 = $X1; + $this->DataSet->Data["GraphArea"]["X1"] = $X1; + $this->GraphAreaY1 = $Y1; + $this->DataSet->Data["GraphArea"]["Y1"] = $Y1; + $this->GraphAreaX2 = $X2; + $this->DataSet->Data["GraphArea"]["X2"] = $X2; + $this->GraphAreaY2 = $Y2; + $this->DataSet->Data["GraphArea"]["Y2"] = $Y2; + } + + /** + * Return the width of the picture + * @return int + */ + public function getWidth() + { + return $this->XSize; + } + + /** + * Return the heigth of the picture + * @return int + */ + public function getHeight() + { + return $this->YSize; + } + + /** + * Render the picture to a file + * @param string $FileName + */ + public function render($FileName) + { + if ($this->TransparentBackground) { + imagealphablending($this->Picture, false); + imagesavealpha($this->Picture, true); + } + imagepng($this->Picture, $FileName); + } + + public function __toString() + { + if ($this->TransparentBackground) { + imagealphablending($this->Picture, false); + imagesavealpha($this->Picture, true); + } + + ob_start(); + imagepng($this->Picture); + return ob_get_clean(); + } + + public function toDataURI() + { + return 'data:image/png;base64,' . base64_encode($this->__toString()); + } + + /** + * Render the picture to a web browser stream + * @param boolean $BrowserExpire + */ + public function stroke($BrowserExpire = false) + { + if ($this->TransparentBackground) { + imagealphablending($this->Picture, false); + imagesavealpha($this->Picture, true); + } + + if ($BrowserExpire) { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Cache-Control: no-cache"); + header("Pragma: no-cache"); + } + + header('Content-type: image/png'); + imagepng($this->Picture); + } + + /** + * Automatic output method based on the calling interface + * @param string $FileName + */ + public function autoOutput($FileName = "output.png") + { + if (php_sapi_name() == "cli") { + $this->Render($FileName); + } else { + $this->Stroke(); + } + } + + /** + * Return the length between two points + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @return float + */ + public function getLength($X1, $Y1, $X2, $Y2) + { + return sqrt( + pow(max($X1, $X2) - min($X1, $X2), 2) + pow(max($Y1, $Y2) - min($Y1, $Y2), 2) + ); + } + + /** + * Return the orientation of a line + * @param int $X1 + * @param int $Y1 + * @param int $X2 + * @param int $Y2 + * @return int + */ + public function getAngle($X1, $Y1, $X2, $Y2) + { + $Opposite = $Y2 - $Y1; + $Adjacent = $X2 - $X1; + $Angle = rad2deg(atan2($Opposite, $Adjacent)); + if ($Angle > 0) { + return $Angle; + } else { + return 360 - abs($Angle); + } + } + + /** + * Return the surrounding box of text area + * @param int $X + * @param int $Y + * @param string $FontName + * @param int $FontSize + * @param int $Angle + * @param int $Text + * @return array + */ + public function getTextBox($X, $Y, $FontName, $FontSize, $Angle, $Text) + { + $coords = imagettfbbox($FontSize, 0, $this->loadFont($FontName, 'fonts'), $Text); + + $a = deg2rad($Angle); + $ca = cos($a); + $sa = sin($a); + $RealPos = []; + for ($i = 0; $i < 7; $i += 2) { + $RealPos[$i / 2]["X"] = $X + round($coords[$i] * $ca + $coords[$i + 1] * $sa); + $RealPos[$i / 2]["Y"] = $Y + round($coords[$i + 1] * $ca - $coords[$i] * $sa); + } + + $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; + $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"]; + $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; + $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"]; + $RealPos[TEXT_ALIGN_TOPLEFT]["X"] = $RealPos[3]["X"]; + $RealPos[TEXT_ALIGN_TOPLEFT]["Y"] = $RealPos[3]["Y"]; + $RealPos[TEXT_ALIGN_TOPRIGHT]["X"] = $RealPos[2]["X"]; + $RealPos[TEXT_ALIGN_TOPRIGHT]["Y"] = $RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["X"] = ($RealPos[1]["X"] - $RealPos[0]["X"]) / 2 + $RealPos[0]["X"]; + $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["Y"] = ($RealPos[0]["Y"] - $RealPos[1]["Y"]) / 2 + $RealPos[1]["Y"]; + $RealPos[TEXT_ALIGN_TOPMIDDLE]["X"] = ($RealPos[2]["X"] - $RealPos[3]["X"]) / 2 + $RealPos[3]["X"]; + $RealPos[TEXT_ALIGN_TOPMIDDLE]["Y"] = ($RealPos[3]["Y"] - $RealPos[2]["Y"]) / 2 + $RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLELEFT]["X"] = ($RealPos[0]["X"] - $RealPos[3]["X"]) / 2 + $RealPos[3]["X"]; + $RealPos[TEXT_ALIGN_MIDDLELEFT]["Y"] = ($RealPos[0]["Y"] - $RealPos[3]["Y"]) / 2 + $RealPos[3]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLERIGHT]["X"] = ($RealPos[1]["X"] - $RealPos[2]["X"]) / 2 + $RealPos[2]["X"]; + $RealPos[TEXT_ALIGN_MIDDLERIGHT]["Y"] = ($RealPos[1]["Y"] - $RealPos[2]["Y"]) / 2 + $RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["X"] = ($RealPos[1]["X"] - $RealPos[3]["X"]) / 2 + $RealPos[3]["X"]; + $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["Y"] = ($RealPos[0]["Y"] - $RealPos[2]["Y"]) / 2 + $RealPos[2]["Y"]; + + return $RealPos; + } + + /** + * Set current font properties + * @param array $Format + */ + public function setFontProperties($Format = []) + { + $R = isset($Format["R"]) ? $Format["R"] : -1; + $G = isset($Format["G"]) ? $Format["G"] : -1; + $B = isset($Format["B"]) ? $Format["B"] : -1; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : null; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : null; + + if ($R != -1) { + $this->FontColorR = $R; + } + if ($G != -1) { + $this->FontColorG = $G; + } + if ($B != -1) { + $this->FontColorB = $B; + } + if ($Alpha != null) { + $this->FontColorA = $Alpha; + } + + if ($FontName != null) { + $this->FontName = $this->loadFont($FontName, 'fonts'); + } + if ($FontSize != null) { + $this->FontSize = $FontSize; + } + } + + /** + * Returns the 1st decimal values (used to correct AA bugs) + * @param mixed $Value + * @return mixed + */ + public function getFirstDecimal($Value) + { + $Values = preg_split("/\./", $Value); + if (isset($Values[1])) { + return substr($Values[1], 0, 1); + } else { + return 0; + } + } + + /** + * Attach a dataset to your pChart Object + * @param Data $DataSet + */ + public function setDataSet(Data $DataSet) + { + $this->DataSet = $DataSet; + } + + /** + * Print attached dataset contents to STDOUT + */ + public function printDataSet() + { + print_r($this->DataSet); + } + + /** + * Initialise the image map methods + * @param string $Name + * @param int $StorageMode + * @param string $UniqueID + * @param string $StorageFolder + */ + public function initialiseImageMap( + $Name = "pChart", + $StorageMode = IMAGE_MAP_STORAGE_SESSION, + $UniqueID = "imageMap", + $StorageFolder = "tmp" + ) { + $this->ImageMapIndex = $Name; + $this->ImageMapStorageMode = $StorageMode; + + if ($StorageMode == IMAGE_MAP_STORAGE_SESSION) { + if (!isset($_SESSION)) { + session_start(); + } + $_SESSION[$this->ImageMapIndex] = null; + } elseif ($StorageMode == IMAGE_MAP_STORAGE_FILE) { + $this->ImageMapFileName = $UniqueID; + $this->ImageMapStorageFolder = $StorageFolder; + + $path = sprintf("%s/%s.map", $StorageFolder, $UniqueID); + if (file_exists($path)) { + unlink($path); + } + } + } + + /** + * Add a zone to the image map + * + * @param string $Type + * @param string $Plots + * @param string|null $Color + * @param string $Title + * @param string $Message + * @param boolean $HTMLEncode + */ + public function addToImageMap( + $Type, + $Plots, + $Color = null, + $Title = null, + $Message = null, + $HTMLEncode = false + ) { + if ($this->ImageMapStorageMode == null) { + $this->initialiseImageMap(); + } + + /* Encode the characters in the imagemap in HTML standards */ + $Title = htmlentities( + str_replace("€", "\u20AC", $Title), + ENT_QUOTES, + "ISO-8859-15" + ); + if ($HTMLEncode) { + $Message = str_replace( + ">", + ">", + str_replace( + "<", + "<", + htmlentities($Message, ENT_QUOTES, "ISO-8859-15") + ) + ); + } + + if ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION) { + if (!isset($_SESSION)) { + $this->initialiseImageMap(); + } + $_SESSION[$this->ImageMapIndex][] = [$Type, $Plots, $Color, $Title, $Message]; + } elseif ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) { + $Handle = fopen( + sprintf("%s/%s.map", $this->ImageMapStorageFolder, $this->ImageMapFileName), + 'a' + ); + fwrite( + $Handle, + sprintf( + "%s%s%s%s%s%s%s%s%s\r\n", + $Type, + IMAGE_MAP_DELIMITER, + $Plots, + IMAGE_MAP_DELIMITER, + $Color, + IMAGE_MAP_DELIMITER, + $Title, + IMAGE_MAP_DELIMITER, + $Message + ) + ); + fclose($Handle); + } + } + + /** + * Remove VOID values from an imagemap custom values array + * @param string $SerieName + * @param array $Values + * @return array + */ + public function removeVOIDFromArray($SerieName, array $Values) + { + if (!isset($this->DataSet->Data["Series"][$SerieName])) { + return -1; + } + + $Result = []; + foreach ($this->DataSet->Data["Series"][$SerieName]["Data"] as $Key => $Value) { + if ($Value != VOID && isset($Values[$Key])) { + $Result[] = $Values[$Key]; + } + } + return $Result; + } + + /** + * Replace the title of one image map serie + * @param string $OldTitle + * @param string|array $NewTitle + * @return null|int + */ + public function replaceImageMapTitle($OldTitle, $NewTitle) + { + if ($this->ImageMapStorageMode == null) { + return -1; + } + + if (is_array($NewTitle)) { + $NewTitle = $this->removeVOIDFromArray($OldTitle, $NewTitle); + } + + if ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION) { + if (!isset($_SESSION)) { + return -1; + } + if (is_array($NewTitle)) { + $ID = 0; + foreach ($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { + if ($Settings[3] == $OldTitle && isset($NewTitle[$ID])) { + $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle[$ID]; + $ID++; + } + } + } else { + foreach ($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { + if ($Settings[3] == $OldTitle) { + $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle; + } + } + } + } elseif ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) { + $TempArray = []; + $Handle = $this->openFileHandle(); + if ($Handle) { + while (($Buffer = fgets($Handle, 4096)) !== false) { + $Fields = preg_split( + sprintf("/%s/", IMAGE_MAP_DELIMITER), + str_replace([chr(10), chr(13)], "", $Buffer) + ); + $TempArray[] = [$Fields[0], $Fields[1], $Fields[2], $Fields[3], $Fields[4]]; + } + fclose($Handle); + + if (is_array($NewTitle)) { + $ID = 0; + foreach ($TempArray as $Key => $Settings) { + if ($Settings[3] == $OldTitle && isset($NewTitle[$ID])) { + $TempArray[$Key][3] = $NewTitle[$ID]; + $ID++; + } + } + } else { + foreach ($TempArray as $Key => $Settings) { + if ($Settings[3] == $OldTitle) { + $TempArray[$Key][3] = $NewTitle; + } + } + } + + $Handle = $this->openFileHandle("w"); + foreach ($TempArray as $Key => $Settings) { + fwrite( + $Handle, + sprintf( + "%s%s%s%s%s%s%s%s%s\r\n", + $Settings[0], + IMAGE_MAP_DELIMITER, + $Settings[1], + IMAGE_MAP_DELIMITER, + $Settings[2], + IMAGE_MAP_DELIMITER, + $Settings[3], + IMAGE_MAP_DELIMITER, + $Settings[4] + ) + ); + } + fclose($Handle); + } + } + } + + /** + * Replace the values of the image map contents + * @param string $Title + * @param array $Values + * @return null|int + */ + public function replaceImageMapValues($Title, array $Values) + { + if ($this->ImageMapStorageMode == null) { + return -1; + } + + $Values = $this->removeVOIDFromArray($Title, $Values); + $ID = 0; + if ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION) { + if (!isset($_SESSION)) { + return -1; + } + foreach ($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { + if ($Settings[3] == $Title) { + if (isset($Values[$ID])) { + $_SESSION[$this->ImageMapIndex][$Key][4] = $Values[$ID]; + } + $ID++; + } + } + } elseif ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) { + $TempArray = []; + $Handle = $this->openFileHandle(); + if ($Handle) { + while (($Buffer = fgets($Handle, 4096)) !== false) { + $Fields = preg_split( + "/" . IMAGE_MAP_DELIMITER . "/", + str_replace([chr(10), chr(13)], "", $Buffer) + ); + $TempArray[] = [$Fields[0], $Fields[1], $Fields[2], $Fields[3], $Fields[4]]; + } + fclose($Handle); + + foreach ($TempArray as $Key => $Settings) { + if ($Settings[3] == $Title) { + if (isset($Values[$ID])) { + $TempArray[$Key][4] = $Values[$ID]; + } + $ID++; + } + } + + $Handle = $this->openFileHandle("w"); + foreach ($TempArray as $Key => $Settings) { + fwrite( + $Handle, + sprintf( + "%s%s%s%s%s%s%s%s%s\r\n", + $Settings[0], + IMAGE_MAP_DELIMITER, + $Settings[1], + IMAGE_MAP_DELIMITER, + $Settings[2], + IMAGE_MAP_DELIMITER, + $Settings[3], + IMAGE_MAP_DELIMITER, + $Settings[4] + ) + ); + } + fclose($Handle); + } + } + } + + /** + * Dump the image map + * @param string $Name + * @param int $StorageMode + * @param string $UniqueID + * @param string $StorageFolder + */ + public function dumpImageMap( + $Name = "pChart", + $StorageMode = IMAGE_MAP_STORAGE_SESSION, + $UniqueID = "imageMap", + $StorageFolder = "tmp" + ) { + $this->ImageMapIndex = $Name; + $this->ImageMapStorageMode = $StorageMode; + + if ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION) { + if (!isset($_SESSION)) { + session_start(); + } + if ($_SESSION[$Name] != null) { + foreach ($_SESSION[$Name] as $Key => $Params) { + echo $Params[0] . IMAGE_MAP_DELIMITER . $Params[1] + . IMAGE_MAP_DELIMITER . $Params[2] . IMAGE_MAP_DELIMITER + . $Params[3] . IMAGE_MAP_DELIMITER . $Params[4] . "\r\n"; + } + } + } elseif ($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) { + if (file_exists($StorageFolder . "/" . $UniqueID . ".map")) { + $Handle = @fopen($StorageFolder . "/" . $UniqueID . ".map", "r"); + if ($Handle) { + while (($Buffer = fgets($Handle, 4096)) !== false) { + echo $Buffer; + } + } + fclose($Handle); + + if ($this->ImageMapAutoDelete) { + unlink($StorageFolder . "/" . $UniqueID . ".map"); + } + } + } + + /* When the image map is returned to the client, the script ends */ + exit(); + } + + /** + * Return the HTML converted color from the RGB composite values + * @param int $R + * @param int $G + * @param int $B + * @return string + */ + public function toHTMLColor($R, $G, $B) + { + $R = intval($R); + $G = intval($G); + $B = intval($B); + $R = dechex($R < 0 ? 0 : ($R > 255 ? 255 : $R)); + $G = dechex($G < 0 ? 0 : ($G > 255 ? 255 : $G)); + $B = dechex($B < 0 ? 0 : ($B > 255 ? 255 : $B)); + $Color = "#" . (strlen($R) < 2 ? '0' : '') . $R; + $Color .= (strlen($G) < 2 ? '0' : '') . $G; + $Color .= (strlen($B) < 2 ? '0' : '') . $B; + + return $Color; + } + + /** + * Reverse an array of points + * @param array $Plots + * @return array + */ + public function reversePlots(array $Plots) + { + $Result = []; + for ($i = count($Plots) - 2; $i >= 0; $i = $i - 2) { + $Result[] = $Plots[$i]; + $Result[] = $Plots[$i + 1]; + } + return $Result; + } + + /** + * Mirror Effect + * @param int $X + * @param int $Y + * @param int $Width + * @param int $Height + * @param array $Format + */ + public function drawAreaMirror($X, $Y, $Width, $Height, array $Format = []) + { + $StartAlpha = isset($Format["StartAlpha"]) ? $Format["StartAlpha"] : 80; + $EndAlpha = isset($Format["EndAlpha"]) ? $Format["EndAlpha"] : 0; + + $AlphaStep = ($StartAlpha - $EndAlpha) / $Height; + + $Picture = imagecreatetruecolor($this->XSize, $this->YSize); + imagecopy($Picture, $this->Picture, 0, 0, 0, 0, $this->XSize, $this->YSize); + + for ($i = 1; $i <= $Height; $i++) { + if ($Y + ($i - 1) < $this->YSize && $Y - $i > 0) { + imagecopymerge( + $Picture, + $this->Picture, + $X, + $Y + ($i - 1), + $X, + $Y - $i, + $Width, + 1, + $StartAlpha - $AlphaStep * $i + ); + } + } + + imagecopy($this->Picture, $Picture, 0, 0, 0, 0, $this->XSize, $this->YSize); + } + + /** + * Open a handle to image storage file. + * @param string $mode + * @return resource + */ + private function openFileHandle($mode = "r") + { + return @fopen( + sprintf("%s/%s.map", $this->ImageMapStorageFolder, $this->ImageMapFileName), + $mode + ); + } +} diff --git a/vendor/szymach/c-pchart/tests/_data/.gitkeep b/vendor/szymach/c-pchart/tests/_data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/szymach/c-pchart/tests/_data/accept.png b/vendor/szymach/c-pchart/tests/_data/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..89c8129a490b329f3165f32fa0781701aab417ea GIT binary patch literal 781 zcmV+o1M>WdP)4-QibtN)VXQDpczE`xXAkUjh%RI>;okxb7K@0kpyQ1k_Y(|Oe7$m(^ zNYX>mI||sUbmn+c3<&FnE=4u#()KBS^SH8e)Qs5i!#lY=$-1gbH6VluzU=m=EP78&5vQ z-?+fFP-G2l&l_QzYealK$;1Rl?FkzXR&Jv@fBPNjCr#AYRyJ7UJQ0v#?)7Ott=>3`#-pV!7>9}>Q1jL)H6h&gkP@3nI=+F3nA~M>u#(n* z8T!#8oEw&-mED4!h4s!N@Jo3S7N&Q6%6l3}nlcd~X@>;uelvPsSkXIgg~e+^T1zSf z3SNj(5%jK~i8@b;CgetChartDirectoryPath(); + if (!is_dir($chartDir)) { + mkdir($chartDir); + } + + $this->clearOutputDirectory(); + } + + public function _afterSuite($settings = []) + { + $this->clearOutputDirectory(); + } + + private function clearOutputDirectory() + { + $this->getFileSystem()->cleanDir($this->getChartDirectoryPath()); + } + + private function getChartDirectoryPath() + { + return sprintf(__DIR__."/../../_output/charts"); + } + + /** + * @return Filesystem + */ + private function getFileSystem() + { + return $this->getModule('Filesystem'); + } +} diff --git a/vendor/szymach/c-pchart/tests/_support/UnitTester.php b/vendor/szymach/c-pchart/tests/_support/UnitTester.php new file mode 100644 index 0000000..e0bba64 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/_support/UnitTester.php @@ -0,0 +1,33 @@ +addPoints(rand(1, 15), 'Probe 1'); + } + $data->setSerieTicks('Probe 2', 4); + $data->setAxisName(0, 'Temperatures'); + $image = new Image(700, 230, $data); + $image->Antialias = false; + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 150, + 35, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(60, 40, 650, 200); + $scaleSettings = [ + 'XMargin' => 10, 'YMargin' => 10, 'Floating' => true, 'GridR' => 200, + 'GridG' => 200, 'GridB' => 200, 'DrawSubTicks' => true, 'CycleBackground' => true + ]; + $image->drawScale($scaleSettings); + $image->drawLegend( + 600, + 20, + ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL] + ); + $image->Antialias = true; + $image->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $threshold = [ + ['Min' => 0, 'Max' => 5, 'R' => 207, 'G' => 240, 'B' => 20, 'Alpha' => 70], + ['Min' => 5, 'Max' => 10, 'R' => 240, 'G' => 232, 'B' => 20, 'Alpha' => 70], + ['Min' => 10, 'Max' => 20, 'R' => 240, 'G' => 191, 'B' => 20, 'Alpha' => 70] + ]; + $image->drawAreaChart(['Threshold' => $threshold]); + $image->drawThreshold( + 5, + [ + 'WriteCaption' => true, 'Caption' => 'Warn Zone', 'Alpha' => 70, 'Ticks' => 2, + 'R' => 0, 'G' => 0, 'B' => 255 + ] + ); + $image->drawThreshold( + 10, + [ + 'WriteCaption' => true, 'Caption' => 'Error Zone', 'Alpha' => 70, 'Ticks' => 2, + 'R' => 0, 'G' => 0, 'B' => 255 + ] + ); + + $filename = $this->tester->getOutputPathForChart('drawAreaChart.threshold.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/BarCodeTest.php b/vendor/szymach/c-pchart/tests/unit/BarCodeTest.php new file mode 100644 index 0000000..46b5e21 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/BarCodeTest.php @@ -0,0 +1,138 @@ + 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'Barcode 39 - Add barcode to your pictures', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $barcode = new Barcode39(); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $settings = ['ShowLegend' => true, 'DrawArea' => true]; + $barcode->draw($image, 'pChart Rocks!', 50, 50, $settings); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 12]); + $settings = ['ShowLegend' => true, 'DrawArea' => true, 'Angle' => 90]; + $barcode->draw($image, 'Turn me on', 650, 50, $settings); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 12]); + $settings = [ + 'R' => 255, 'G' => 255, 'B' => 255, 'AreaR' => 150, 'AreaG' => 30, + 'AreaB' => 27, 'ShowLegend' => true, 'DrawArea' => true, 'Angle' => 350, + 'AreaBorderR' => 70, 'AreaBorderG' => 20, 'AreaBorderB' => 20 + ]; + $barcode->draw($image, 'Do what you want !', 290, 140, $settings); + + $filename = $this->tester->getOutputPathForChart('drawBarcode39.png'); + $barcode->pChartObject->render($filename); + $barcode->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function test128Code() + { + $image = new Image(700, 230); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'Barcode 128 - Add barcode to your pictures', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $barcode = new Barcode128(); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $settings = ['ShowLegend' => true, 'DrawArea' => true]; + $barcode->draw($image, 'pChart Rocks!', 50, 50, $settings); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 12]); + $settings = ['ShowLegend' => true, 'DrawArea' => true, 'Angle' => 90]; + $barcode->draw($image, 'Turn me on', 650, 50, $settings); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 12]); + $settings = [ + 'R' => 255, 'G' => 255, 'B' => 255, 'AreaR' => 150, 'AreaG' => 30, + 'AreaB' => 27, 'ShowLegend' => true, 'DrawArea' => true, 'Angle' => 350, + 'AreaBorderR' => 70, 'AreaBorderG' => 20, 'AreaBorderB' => 20 + ]; + $barcode->draw($image, 'Do what you want !', 290, 140, $settings); + + $filename = $this->tester->getOutputPathForChart('drawBarcode128.png'); + $barcode->pChartObject->render($filename); + $barcode->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/BarTest.php b/vendor/szymach/c-pchart/tests/unit/BarTest.php new file mode 100644 index 0000000..1b1c19d --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/BarTest.php @@ -0,0 +1,110 @@ +addPoints([-4, VOID, VOID, 12, 8, 3], 'Probe 1'); + $data->addPoints([3, 12, 15, 8, 5, -5], 'Probe 2'); + $data->addPoints([2, 0, 5, 18, 19, 22], 'Probe 3'); + $data->setSerieTicks('Probe 2', 4); + $data->setAxisName(0, 'Temperatures'); + $data->addPoints(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 'Labels'); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data); + $image->drawFilledRectangle( + 0, + 0, + 700, + 230, + ['R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, 'DashG' => 203, 'DashB' => 107] + ); + $settings = [ + 'StartR' => 219, + 'StartG' => 231, + 'StartB' => 139, + 'EndR' => 1, + 'EndG' => 138, + 'EndB' => 68, + 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + ['StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, 'EndB' => 50, 'Alpha' => 80] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText(10, 13, 'drawBarChart() - draw a bar chart', ['R' => 255, 'G' => 255, 'B' => 255]); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 250, + 55, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setGraphArea(60, 60, 450, 190); + $image->drawFilledRectangle( + 60, + 60, + 450, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true]); + $image->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawBarChart([ + 'DisplayValues' => true, + 'DisplayColor' => DISPLAY_AUTO, + 'Rounded' => true, + 'Surrounding' => 60 + ]); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $image->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $image->drawBarChart(); + $image->setShadow(false); + $image->drawLegend(510, 205, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawBarChart.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/BestFitTest.php b/vendor/szymach/c-pchart/tests/unit/BestFitTest.php new file mode 100644 index 0000000..38a8ba5 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/BestFitTest.php @@ -0,0 +1,64 @@ +addPoints(rand(10, 30) + $i, 'Probe 1'); + } + for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(0, 10) + $i, 'Probe 2'); + } + $data->setAxisName(0, 'Temperatures'); + $image = new Image(700, 230, $data); + $image->Antialias = false; + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 150, + 35, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(60, 40, 650, 200); + $scaleSettings = [ + 'XMargin' => 10, 'YMargin' => 10, 'Floating' => true, 'GridR' => 200, + 'GridG' => 200, 'GridB' => 200, 'DrawSubTicks' => true, 'CycleBackground' => true + ]; + $image->drawScale($scaleSettings); + $image->Antialias = true; + $image->drawBestFit(); + $image->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $image->drawPlotChart(); + $image->drawLegend( + 580, + 20, + ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL] + ); + + $filename = $this->tester->getOutputPathForChart('drawBestFit.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/BubbleTest.php b/vendor/szymach/c-pchart/tests/unit/BubbleTest.php new file mode 100644 index 0000000..d8e89fc --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/BubbleTest.php @@ -0,0 +1,119 @@ +addPoints([34, 55, 15, 62, 38, 42], 'Probe1'); + $data->addPoints([5, 10, 8, 9, 15, 10], 'Probe1Weight'); + $data->addPoints([5, 10, -5, -1, 0, -10], 'Probe2'); + $data->addPoints([6, 10, 14, 10, 14, 6], 'Probe2Weight'); + $data->setSerieDescription('Probe1', 'This year'); + $data->setSerieDescription('Probe2', 'Last year'); + $data->setAxisName(0, 'Current stock'); + $data->addPoints( + ['Apple', 'Banana', 'Orange', 'Lemon', 'Peach', 'Strawberry'], + 'Product' + ); + $data->setAbscissa('Product'); + + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawBubbleChart() - draw a linear bubble chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 40, + 55, + 'Current Stock / Needs chart', + ['FontSize' => 14, 'Align' => TEXT_ALIGN_BOTTOMLEFT] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $bubbleChart = new Bubble($image, $data); + $bubbleDataSeries = ['Probe1', 'Probe2']; + $bubbleWeightSeries = ['Probe1Weight', 'Probe2Weight']; + $bubbleChart->bubbleScale($bubbleDataSeries, $bubbleWeightSeries); + $image->setGraphArea(40, 60, 430, 190); + $image->drawFilledRectangle( + 40, + 60, + 430, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true, 'CycleBackground' => true]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 30] + ); + $bubbleChart->drawBubbleChart($bubbleDataSeries, $bubbleWeightSeries); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 30] + ); + $bubbleChart->drawbubbleChart($bubbleDataSeries, $bubbleWeightSeries); + $image->drawLegend(550, 215, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawBubble.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/CacheTest.php b/vendor/szymach/c-pchart/tests/unit/CacheTest.php new file mode 100644 index 0000000..0a40840 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/CacheTest.php @@ -0,0 +1,134 @@ +createImageData(); + + // Write to cache + $cache = new Cache(); + $chartHash = $cache->getHash($data); + $cache->writeToCache($chartHash, $image); + $this->tester->seeFileFound($this->getCacheFilePath('cache.db')); + $this->tester->seeFileFound($this->getCacheFilePath('index.db')); + $this->tester->assertEquals(true, $cache->isInCache($chartHash)); + + // Render and then remove the chart + $filename = $this->tester->getOutputPathForChart('drawCachedSpline.png'); + $image->render($filename); + $this->tester->seeFileFound($filename); + $this->tester->deleteFile($filename); + $this->tester->cantSeeFileFound($filename); + + // Test retrieving image from cache + $cache->saveFromCache($chartHash, $filename); + $this->tester->seeFileFound($filename); + $this->tester->assertEquals(true, $cache->strokeFromCache($chartHash)); + } + + public function testRemovalOperations() + { + list($data, $image) = $this->createImageData(); + + // Write to cache + $cache = new Cache(); + $chartHash = $cache->getHash($data); + $cache->writeToCache($chartHash, $image); + $this->tester->assertEquals(true, $cache->isInCache($chartHash)); + + // Remove by name + $cache->remove($chartHash); + $this->tester->assertEquals(false, $cache->isInCache($chartHash)); + + // Remove older than x seconds + $cache->writeToCache($chartHash, $image); + $this->tester->assertEquals(true, $cache->isInCache($chartHash)); + $cache->removeOlderThan(4); + $this->tester->assertEquals(true, $cache->isInCache($chartHash)); + sleep(5); + $cache->removeOlderThan(4); + $this->tester->assertEquals(false, $cache->isInCache($chartHash)); + + // Flush the cache + $cache->writeToCache($chartHash, $image); + $this->tester->assertEquals(true, $cache->isInCache($chartHash)); + $cache->flush(); + $this->tester->assertEquals(false, $cache->isInCache($chartHash)); + } + + protected function _before() + { + $this->clearCache(); + } + + protected function _after() + { + $this->clearCache(); + } + + private function createImageData() + { + $data = new Data(); + $data->addPoints([1, 3, 4, 3, 5]); + + $image = new Image(700, 230, $data); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->setGraphArea(60, 40, 670, 190); + $image->drawScale(); + $image->drawSplineChart(); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'Test of the pCache class', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + + return [$data, $image]; + } + + private function clearCache() + { + foreach (['cache.db', 'index.db'] as $cacheFile) { + $filename = $this->getCacheFilePath($cacheFile); + if (true === file_exists($filename)) { + unlink($filename); + } + } + } + + /** + * @param string $filename + * @return string + */ + private function getCacheFilePath($filename) + { + return sprintf('%s/%s', $this->tester->getCacheDirectory(), $filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/FilledSplineTest.php b/vendor/szymach/c-pchart/tests/unit/FilledSplineTest.php new file mode 100644 index 0000000..1560b10 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/FilledSplineTest.php @@ -0,0 +1,110 @@ +setAxisName(0, 'Strength'); + for ($i = 0; $i <= 720; $i = $i + 20) { + $data->addPoints(cos(deg2rad($i)) * 100, 'Probe 1'); + $data->addPoints(cos(deg2rad($i + 90)) * 60, 'Probe 2'); + } + $image = new Image(847, 304, $data); + $image->drawGradientArea( + 0, + 0, + 847, + 304, + DIRECTION_VERTICAL, + [ + 'StartR' => 47, 'StartG' => 47, 'StartB' => 47, 'EndR' => 17, 'EndG' => 17, + 'EndB' => 17, 'Alpha' => 100 + ] + ); + $image->drawGradientArea( + 0, + 250, + 847, + 304, + DIRECTION_VERTICAL, + [ + 'StartR' => 47, 'StartG' => 47, 'StartB' => 47, 'EndR' => 27, 'EndG' => 27, + 'EndB' => 27, 'Alpha' => 100 + ] + ); + $image->drawLine(0, 249, 847, 249, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->drawLine(0, 250, 847, 250, ['R' => 70, 'G' => 70, 'B' => 70]); + $image->drawRectangle(0, 0, 846, 303, ['R' => 204, 'G' => 204, 'B' => 204]); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawText( + 423, + 14, + 'Cyclic magnetic field strength', + ['R' => 255, 'G' => 255, 'B' => 255, 'Align' => TEXT_ALIGN_MIDDLEMIDDLE] + ); + $image->setGraphArea(58, 27, 816, 228); + $image->drawFilledRectangle( + 58, + 27, + 816, + 228, + [ + 'R' => 0, 'G' => 0, 'B' => 0, 'Dash' => true, 'DashR' => 0, 'DashG' => 51, + 'DashB' => 51, 'BorderR' => 0, 'BorderG' => 0, 'BorderB' => 0 + ] + ); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 20] + ); + $image->setFontProperties(['R' => 255, 'G' => 255, 'B' => 255]); + $ScaleSettings = [ + 'XMargin' => 4, 'DrawSubTicks' => true, 'GridR' => 255, + 'GridG' => 255, 'GridB' => 255, 'AxisR' => 255, 'AxisG' => 255, 'AxisB' => 255, + 'GridAlpha' => 30, 'CycleBackground' => true + ]; + $image->drawScale($ScaleSettings); + $image->drawFilledSplineChart(); + $BoundsSettings = [ + 'MaxDisplayR' => 237, 'MaxDisplayG' => 23, 'MaxDisplayB' => 48, + 'MinDisplayR' => 23, 'MinDisplayG' => 144, 'MinDisplayB' => 237 + ]; + $image->writeBounds(BOUND_BOTH, $BoundsSettings); + $image->drawThreshold(0, ['WriteCaption' => true]); + $image->setFontProperties(['R' => 255, 'G' => 255, 'B' => 255]); + $image->drawLegend(560, 266, ['Style' => LEGEND_NOBORDER]); + $settings = ['R' => 188, 'G' => 224, 'B' => 46, 'Align' => TEXT_ALIGN_BOTTOMLEFT]; + $image->drawText(620, 270, 'Max : ' . ceil($data->getMax('Probe 1')), $settings); + $image->drawText(680, 270, 'Min : ' . ceil($data->getMin('Probe 1')), $settings); + $image->drawText(740, 270, 'Avg : ' . ceil($data->getSerieAverage('Probe 1')), $settings); + $settings = ['R' => 224, 'G' => 100, 'B' => 46, 'Align' => TEXT_ALIGN_BOTTOMLEFT]; + $image->drawText(620, 283, 'Max : ' . ceil($data->getMax('Probe 2')), $settings); + $image->drawText(680, 283, 'Min : ' . ceil($data->getMin('Probe 2')), $settings); + $image->drawText(740, 283, 'Avg : ' . ceil($data->getSerieAverage('Probe 2')), $settings); + + $filename = $this->tester->getOutputPathForChart('drawFilledSplineChart.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/FilledStepTest.php b/vendor/szymach/c-pchart/tests/unit/FilledStepTest.php new file mode 100644 index 0000000..374034d --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/FilledStepTest.php @@ -0,0 +1,116 @@ +addPoints([-4, 2, VOID, 12, 8, 3], 'Probe 1'); + $data->addPoints([3, 12, 15, 8, 5, -5], 'Probe 2'); + $data->addPoints([2, 7, 5, 18, 19, 22], 'Probe 3'); + $data->setSerieTicks('Probe 2', 4); + $data->setAxisName(0, 'Temperatures'); + $data->addPoints(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 'Labels'); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawFilledStepChart() - draw a filled step chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 250, + 55, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setGraphArea(60, 60, 450, 190); + $image->drawFilledRectangle( + 60, + 60, + 450, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawFilledStepChart([ + 'ForceTransparency' => 40, + 'DisplayValues' => true, + 'DisplayColor' => DISPLAY_AUTO + ]); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => -1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->drawFilledStepChart(['ForceTransparency' => 40]); + $image->setShadow(false); + $image->drawLegend(510, 205, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawFilledStepChart.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/LineTest.php b/vendor/szymach/c-pchart/tests/unit/LineTest.php new file mode 100644 index 0000000..12cfcde --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/LineTest.php @@ -0,0 +1,76 @@ +addPoints([-4, VOID, VOID, 12, 8, 3], 'Probe 1'); + $data->addPoints([3, 12, 15, 8, 5, -5], 'Probe 2'); + $data->addPoints([2, 7, 5, 18, 19, 22], 'Probe 3'); + $data->setSerieTicks('Probe 2', 4); + $data->setSerieWeight('Probe 3', 2); + $data->setAxisName(0, 'Temperatures'); + $data->addPoints(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 'Labels'); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + + $image = new Image(700, 230, $data); + $image->setGraphArea(60, 60, 450, 190); + $image->drawFilledRectangle( + 60, + 60, + 450, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawLineChart(['DisplayValues' => true, 'DisplayColor' => DISPLAY_AUTO]); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => -1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->drawLineChart(); + $image->setShadow(false); + $image->drawLegend(510, 205, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawLine.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/PieTest.php b/vendor/szymach/c-pchart/tests/unit/PieTest.php new file mode 100644 index 0000000..79b0a3d --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/PieTest.php @@ -0,0 +1,343 @@ +addPoints([40, 60, 15, 10, 6, 4], 'ScoreA'); + $data->setSerieDescription('ScoreA', 'Application A'); + $data->addPoints(['<10', '10<>20', '20<>40', '40<>60', '60<>80', '>80'], 'Labels'); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data); + $image->drawFilledRectangle( + 0, + 0, + 700, + 230, + [ + 'R' => 173, 'G' => 152, 'B' => 217, 'Dash' => 1, 'DashR' => 193, + 'DashG' => 172, 'DashB' => 237 + ] + ); + $image->drawGradientArea( + 0, + 0, + 700, + 230, + DIRECTION_VERTICAL, + [ + 'StartR' => 209, 'StartG' => 150, 'StartB' => 231, 'EndR' => 111, + 'EndG' => 3, 'EndB' => 138, 'Alpha' => 50 + ] + ); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pPie - Draw 2D pie charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties( + ['FontName' => 'Forgotte.ttf', 'FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80] + ); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 150, 'G' => 150, 'B' => 150, 'Alpha' => 100] + ); + $pieChart = new Pie($image, $data); + $pieChart->draw2DPie(140, 125, ['SecondPass' => false]); + $pieChart->draw2DPie(340, 125, ['DrawLabels' => true, 'Border' => true]); + $pieChart->draw2DPie( + 540, + 125, + [ + 'DataGapAngle' => 10, 'DataGapRadius' => 6, 'Border' => true, + 'BorderR' => 255, 'BorderG' => 255, 'BorderB' => 255 + ] + ); + + $image->setFontProperties(['FontName' => 'MankSans.ttf', 'FontSize' => 11]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 20] + ); + $image->drawText( + 140, + 200, + 'Single AA pass', + ['R' => 0, 'G' => 0, 'B' => 0, 'Align' => TEXT_ALIGN_TOPMIDDLE] + ); + $image->drawText( + 540, + 200, + 'Extended AA pass / Splitted', + ['R' => 0, 'G' => 0, 'B' => 0, 'Align' => TEXT_ALIGN_TOPMIDDLE] + ); + + $filename = $this->tester->getOutputPathForChart('draw2DPie.png'); + $pieChart->pChartObject->render($filename); + $pieChart->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function test2dRingRender() + { + $data = new Data(); + $data->addPoints([50, 2, 3, 4, 7, 10, 25, 48, 41, 10], 'ScoreA'); + $data->setSerieDescription('ScoreA', 'Application A'); + $data->addPoints( + ['A0', 'B1', 'C2', 'D3', 'E4', 'F5', 'G6', 'H7', 'I8', 'J9'], + 'Labels' + ); + $data->setAbscissa('Labels'); + $image = new Image(300, 260, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 300, 300, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 300, 260, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 300, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 299, 259, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pPie - Draw 2D ring charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties([ + 'FontName' => 'Forgotte.ttf', 'FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80 + ]); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 50] + ); + $pieChart = new Pie($image, $data); + $pieChart->draw2DRing( + 160, + 140, + ['DrawLabels' => true, 'LabelStacked' => true, 'Border' => true] + ); + $image->setShadow(false); + $pieChart->drawPieLegend(15, 40, ['Alpha' => 20]); + + $filename = $this->tester->getOutputPathForChart('draw2DRing.png'); + $pieChart->pChartObject->render($filename); + $pieChart->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function test3dPieRender() + { + $data = new Data(); + $data->addPoints([40, 30, 20], 'ScoreA'); + $data->setSerieDescription('ScoreA', 'Application A'); + $data->addPoints(['A', 'B', 'C'], 'Labels'); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data, true); + $settings = [ + 'R' => 173, 'G' => 152, 'B' => 217, 'Dash' => 1, 'DashR' => 193, + 'DashG' => 172, 'DashB' => 237 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 209, 'StartG' => 150, 'StartB' => 231, 'EndR' => 111, + 'EndG' => 3, 'EndB' => 138, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pPie - Draw 3D pie charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties([ + 'FontName' => 'Forgotte.ttf', 'FontSize' => 10, + 'R' => 80, 'G' => 80, 'B' => 80 + ]); + $pieChart = new Pie($image, $data); + $pieChart->setSliceColor(0, ['R' => 143, 'G' => 197, 'B' => 0]); + $pieChart->setSliceColor(1, ['R' => 97, 'G' => 77, 'B' => 63]); + $pieChart->setSliceColor(2, ['R' => 97, 'G' => 113, 'B' => 63]); + $pieChart->draw3DPie(120, 125, ['SecondPass' => false]); + $pieChart->draw3DPie(340, 125, ['DrawLabels' => true, 'Border' => true]); + $image->setShadow( + true, + ['X' => 3, 'Y' => 3, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $pieChart->draw3DPie( + 560, + 125, + ['WriteValues' => true, 'DataGapAngle' => 10, 'DataGapRadius' => 6, 'Border' => true] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 20] + ); + $image->drawText( + 120, + 200, + 'Single AA pass', + [ + 'DrawBox' => true, 'BoxRounded' => true, 'R' => 0, 'G' => 0, 'B' => 0, + 'Align' => TEXT_ALIGN_TOPMIDDLE + ] + ); + $image->drawText( + 440, + 200, + 'Extended AA pass / Splitted', + [ + 'DrawBox' => true, 'BoxRounded' => true, 'R' => 0, 'G' => 0, 'B' => 0, + 'Align' => TEXT_ALIGN_TOPMIDDLE + ] + ); + $image->setFontProperties([ + 'FontName' => 'Silkscreen.ttf', 'FontSize' => 6, + 'R' => 255, 'G' => 255, 'B' => 255 + ]); + $pieChart->drawPieLegend( + 600, + 8, + ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL] + ); + + $filename = $this->tester->getOutputPathForChart('draw3DPie.png'); + $pieChart->pChartObject->render($filename); + $pieChart->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function test3dRingRender() + { + $data = new Data(); + $data->addPoints([50, 2, 3, 4, 7, 10, 25, 48, 41, 10], 'ScoreA'); + $data->setSerieDescription('ScoreA', 'Application A'); + $data->addPoints( + ['A0', 'B1', 'C2', 'D3', 'E4', 'F5', 'G6', 'H7', 'I8', 'J9'], + 'Labels' + ); + $data->setAbscissa('Labels'); + $image = new Image(400, 400, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pPie - Draw 3D ring charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties([ + 'FontName' => 'Forgotte.ttf', + 'FontSize' => 10, + 'R' => 80, + 'G' => 80, + 'B' => 80 + ]); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 50] + ); + $pieChart = new Pie($image, $data); + $pieChart->draw3DRing( + 200, + 200, + ['DrawLabels' => true, 'LabelStacked' => true, 'Border' => true] + ); + $pieChart->drawPieLegend( + 80, + 360, + ['Mode' => LEGEND_HORIZONTAL, 'Style' => LEGEND_NOBORDER, 'Alpha' => 20] + ); + + $filename = $this->tester->getOutputPathForChart('draw3DRing.png'); + $pieChart->pChartObject->render($filename); + $pieChart->pChartObject->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/PlotTest.php b/vendor/szymach/c-pchart/tests/unit/PlotTest.php new file mode 100644 index 0000000..859793d --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/PlotTest.php @@ -0,0 +1,65 @@ +addPoints(rand(0, 20), 'Probe 1'); + } + for ($i = 0; $i <= 20; $i++) { + $data->addPoints(rand(0, 20), 'Probe 2'); + } + $data->setSerieShape('Probe 1', SERIE_SHAPE_FILLEDTRIANGLE); + $data->setSerieShape('Probe 2', SERIE_SHAPE_FILLEDSQUARE); + $data->setAxisName(0, 'Temperatures'); + $image = new Image(700, 230, $data); + $image->Antialias = false; + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 150, + 35, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(60, 40, 650, 200); + $scaleSettings = [ + 'XMargin' => 10, 'YMargin' => 10, 'Floating' => true, 'GridR' => 200, + 'GridG' => 200, 'GridB' => 200, 'DrawSubTicks' => true, 'CycleBackground' => true + ]; + $image->drawScale($scaleSettings); + $image->Antialias = true; + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->drawPlotChart(); + $image->drawLegend(580, 20, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + $filename = $this->tester->getOutputPathForChart('drawPlot.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/PolarTest.php b/vendor/szymach/c-pchart/tests/unit/PolarTest.php new file mode 100644 index 0000000..f37bacb --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/PolarTest.php @@ -0,0 +1,113 @@ +addPoints([10, 20, 30, 40, 50, 60, 70, 80, 90], 'ScoreA'); + $data->addPoints([20, 40, 50, 12, 10, 30, 40, 50, 60], 'ScoreB'); + $data->setSerieDescription('ScoreA', 'Coverage A'); + $data->setSerieDescription('ScoreB', 'Coverage B'); + $data->addPoints([40, 80, 120, 160, 200, 240, 280, 320, 360], 'Coord'); + $data->setAbscissa('Coord'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 179, 'G' => 217, 'B' => 91, 'Dash' => 1, 'DashR' => 199, + 'DashG' => 237, 'DashB' => 111 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 194, 'StartG' => 231, 'StartB' => 44, 'EndR' => 43, + 'EndG' => 107, 'EndB' => 58, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pRadar - Draw polar charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties( + ['FontName' => 'Forgotte.ttf', 'FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80] + ); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $splitChart = new Radar(); + $image->setGraphArea(10, 25, 340, 225); + $options = [ + 'BackgroundGradient' => [ + 'StartR' => 255, + 'StartG' => 255, + 'StartB' => 255, + 'StartAlpha' => 100, + 'EndR' => 207, + 'EndG' => 227, + 'EndB' => 125, + 'EndAlpha' => 50 + ] + ]; + $splitChart->drawPolar($image, $data, $options); + $image->setGraphArea(350, 25, 690, 225); + $options = [ + 'LabelPos' => RADAR_LABELS_HORIZONTAL, + 'BackgroundGradient' => [ + 'StartR' => 255, + 'StartG' => 255, + 'StartB' => 255, + 'StartAlpha' => 50, + 'EndR' => 32, + 'EndG' => 109, + 'EndB' => 174, + 'EndAlpha' => 30 + ], + 'AxisRotation' => 0, + 'DrawPoly' => true, + 'PolyAlpha' => 50 + ]; + $splitChart->drawPolar($image, $data, $options); + + // Write the chart legend + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawLegend(270, 205, ['Style' => LEGEND_BOX, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawPolar.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/ProgressTest.php b/vendor/szymach/c-pchart/tests/unit/ProgressTest.php new file mode 100644 index 0000000..4a4c865 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/ProgressTest.php @@ -0,0 +1,96 @@ +setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 20] + ); + + /* Left Red bar */ + $progressOptions = [ + 'R' => 209, 'G' => 31, 'B' => 27, 'Surrounding' => 20, + 'BoxBorderR' => 0, 'BoxBorderG' => 0, 'BoxBorderB' => 0, 'BoxBackR' => 255, + 'BoxBackG' => 255, 'BoxBackB' => 255, 'RFade' => 206, 'GFade' => 133, + 'BFade' => 30, 'ShowLabel' => true + ]; + $image->drawProgress(40, 60, 77, $progressOptions); + $progressOptions = [ + 'Width' => 165, 'R' => 209, 'G' => 125, 'B' => 27, 'Surrounding' => 20, + 'BoxBorderR' => 0, 'BoxBorderG' => 0, 'BoxBorderB' => 0, 'BoxBackR' => 255, + 'BoxBackG' => 255, 'BoxBackB' => 255, 'NoAngle' => true, 'ShowLabel' => true, + 'LabelPos' => LABEL_POS_RIGHT + ]; + $image->drawProgress(40, 100, 50, $progressOptions); + $progressOptions = [ + 'Width' => 165, 'R' => 209, 'G' => 198, 'B' => 27, 'Surrounding' => 20, + 'BoxBorderR' => 0, 'BoxBorderG' => 0, 'BoxBorderB' => 0, 'BoxBackR' => 255, + 'BoxBackG' => 255, 'BoxBackB' => 255, 'ShowLabel' => true, 'LabelPos' => LABEL_POS_LEFT + ]; + $image->drawProgress(75, 140, 25, $progressOptions); + $progressOptions = ['Width' => 400, 'R' => 134, 'G' => 209, 'B' => 27, 'Surrounding' => 20, + 'BoxBorderR' => 0, 'BoxBorderG' => 0, 'BoxBorderB' => 0, 'BoxBackR' => 255, + 'BoxBackG' => 255, 'BoxBackB' => 255, 'RFade' => 206, 'GFade' => 133, + 'BFade' => 30, 'ShowLabel' => true, 'LabelPos' => LABEL_POS_CENTER]; + $image->drawProgress(40, 180, 80, $progressOptions); + $progressOptions = [ + 'Width' => 20, 'Height' => 150, 'R' => 209, 'G' => 31, + 'B' => 27, 'Surrounding' => 20, 'BoxBorderR' => 0, 'BoxBorderG' => 0, + 'BoxBorderB' => 0, 'BoxBackR' => 255, 'BoxBackG' => 255, 'BoxBackB' => 255, + 'RFade' => 206, 'GFade' => 133, 'BFade' => 30, 'ShowLabel' => true, 'Orientation' => ORIENTATION_VERTICAL, + 'LabelPos' => LABEL_POS_BOTTOM + ]; + $image->drawProgress(500, 200, 77, $progressOptions); + $progressOptions = [ + 'Width' => 20, 'Height' => 150, 'R' => 209, 'G' => 125, + 'B' => 27, 'Surrounding' => 20, 'BoxBorderR' => 0, 'BoxBorderG' => 0, + 'BoxBorderB' => 0, 'BoxBackR' => 255, 'BoxBackG' => 255, 'BoxBackB' => 255, + 'NoAngle' => true, 'ShowLabel' => true, 'Orientation' => ORIENTATION_VERTICAL, + 'LabelPos' => LABEL_POS_TOP + ]; + $image->drawProgress(540, 200, 50, $progressOptions); + $progressOptions = [ + 'Width' => 20, 'Height' => 150, 'R' => 209, 'G' => 198, + 'B' => 27, 'Surrounding' => 20, 'BoxBorderR' => 0, 'BoxBorderG' => 0, + 'BoxBorderB' => 0, 'BoxBackR' => 255, 'BoxBackG' => 255, 'BoxBackB' => 255, + 'ShowLabel' => true, 'Orientation' => ORIENTATION_VERTICAL, 'LabelPos' => LABEL_POS_INSIDE + ]; + $image->drawProgress(580, 200, 25, $progressOptions); + $progressOptions = [ + 'Width' => 20, 'Height' => 150, 'R' => 134, 'G' => 209, + 'B' => 27, 'Surrounding' => 20, 'BoxBorderR' => 0, 'BoxBorderG' => 0, + 'BoxBorderB' => 0, 'BoxBackR' => 255, 'BoxBackG' => 255, 'BoxBackB' => 255, + 'RFade' => 206, 'GFade' => 133, 'BFade' => 30, 'ShowLabel' => true, + 'Orientation' => ORIENTATION_VERTICAL, 'LabelPos' => LABEL_POS_CENTER + ]; + $image->drawProgress(620, 200, 80, $progressOptions); + + $filename = $this->tester->getOutputPathForChart('drawProgress.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/RadarTest.php b/vendor/szymach/c-pchart/tests/unit/RadarTest.php new file mode 100644 index 0000000..d98e550 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/RadarTest.php @@ -0,0 +1,107 @@ +addPoints([40, 20, 15, 10, 8, 4], 'ScoreA'); + $data->addPoints([8, 10, 12, 20, 30, 15], 'ScoreB'); + $data->setSerieDescription('ScoreA', 'Application A'); + $data->setSerieDescription('ScoreB', 'Application B'); + $data->addPoints( + ['Size', 'Speed', 'Reliability', 'Functionalities', 'Ease of use', 'Weight'], + 'Labels' + ); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 179, 'G' => 217, 'B' => 91, 'Dash' => 1, 'DashR' => 199, + 'DashG' => 237, 'DashB' => 111 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 194, 'StartG' => 231, 'StartB' => 44, 'EndR' => 43, + 'EndG' => 107, 'EndB' => 58, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pRadar - Draw radar charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties( + ['FontName' => 'Forgotte.ttf', 'FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80] + ); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $radarChart = new Radar(); + $image->setGraphArea(10, 25, 340, 225); + $options = [ + 'Layout' => RADAR_LAYOUT_STAR, + 'BackgroundGradient' => [ + 'StartR' => 255, + 'StartG' => 255, + 'StartB' => 255, + 'StartAlpha' => 100, + 'EndR' => 207, + 'EndG' => 227, 'EndB' => 125, 'EndAlpha' => 50 + ] + ]; + $radarChart->drawRadar($image, $data, $options); + $image->setGraphArea(350, 25, 690, 225); + $options = [ + 'Layout' => RADAR_LAYOUT_CIRCLE, + 'LabelPos' => RADAR_LABELS_HORIZONTAL, + 'BackgroundGradient' => [ + 'StartR' => 255, 'StartG' => 255, 'StartB' => 255, + 'StartAlpha' => 50, 'EndR' => 32, 'EndG' => 109, 'EndB' => 174, 'EndAlpha' => 30 + ] + ]; + $radarChart->drawRadar($image, $data, $options); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawLegend(270, 205, ['Style' => LEGEND_BOX, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawRadar.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/RegressionTest.php b/vendor/szymach/c-pchart/tests/unit/RegressionTest.php new file mode 100644 index 0000000..91a4175 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/RegressionTest.php @@ -0,0 +1,69 @@ +addPoints($values, 'Score'); + $data->setSerieDescription('Score', '222'); + + $data->addPoints(['111', '222', '333', '444', '555'], 'Labels'); + $data->setAbscissa('Labels'); + $data->setPalette('Score', ['R' => 157, 'G' => 96, 'B' => 22]); + + $image = new Image($width, $height, $data); + $image->setFontProperties(['FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80]); + + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + + $radar = new Radar; + $image->setGraphArea(0, 0, $width, $height); + $options = [ + 'DrawPoly' => true, + 'WriteValues' => true, + 'ValueFontSize' => 7, + 'Layout' => RADAR_LAYOUT_CIRCLE, + 'BackgroundGradient' => [ + 'StartR' => 255, + 'StartG' => 255, + 'StartB' => 255, + 'StartAlpha' => 100, + 'EndR' => 207, + 'EndG' => 227, + 'EndB' => 125, + 'EndAlpha' => 50 + ] + ]; + + $radar->drawRadar($image, $data, $options); + $filename = $this->tester->getOutputPathForChart('drawRadarFractional.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/ResourceTest.php b/vendor/szymach/c-pchart/tests/unit/ResourceTest.php new file mode 100644 index 0000000..d6b83f6 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/ResourceTest.php @@ -0,0 +1,73 @@ +tester->expectThrowable( + '\Exception', + function () use ($data) { + $data->loadPalette('nonExistantPalette'); + } + ); + + $image = new Image(700, 230, $data); + + $this->tester->expectThrowable( + '\Exception', + function () use ($image) { + $image->setResourcePath('nonExistantDirectory'); + } + ); + $this->tester->expectThrowable( + '\Exception', + function () use ($image) { + $image->setFontProperties(['FontName' => 'nonExistantFont']); + } + ); + $this->tester->expectThrowable( + '\Exception', + function () use ($image) { + $image->getLegendSize(['Font' => 'nonExistantFont']); + } + ); + } + + public function testValidPaletteLoading() + { + $data = new Data(); + $data->loadPalette(sprintf('%s/../_data/test_palette.txt', __DIR__), true); + + $image = new Image(700, 230, $data); + $firstCoordinates = [[40, 80], [280, 60], [340, 166], [590, 120]]; + $fistSplineSettings = ['R' => 255, 'G' => 255, 'B' => 255, 'ShowControl' => true]; + $image->drawSpline($firstCoordinates, $fistSplineSettings); + $filename = $this->tester->getOutputPathForChart('drawSpline.png'); + $image->render($filename); + $this->tester->seeFileFound($filename); + } + + public function testInvalidPaletteLoading() + { + $data = new Data(); + $this->tester->expectThrowable( + '\Exception', + function () use ($data) { + $data->loadPalette(sprintf('non_existant_palette', __DIR__), true); + } + ); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/ScatterTest.php b/vendor/szymach/c-pchart/tests/unit/ScatterTest.php new file mode 100644 index 0000000..80ccea3 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/ScatterTest.php @@ -0,0 +1,327 @@ +addPoints(rand(1, 20) * 10 + rand(0, $i), 'Probe 1'); + } + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(rand(1, 2) * 10 + rand(0, $i), 'Probe 2'); + } + $data->setAxisName(0, 'X-Index'); + $data->setAxisXY(0, AXIS_X); + $data->setAxisPosition(0, AXIS_POSITION_TOP); + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, 'Probe 3'); + } + $data->setSerieOnAxis('Probe 3', 1); + $data->setAxisName(1, 'Y-Index'); + $data->setAxisXY(1, AXIS_Y); + $data->setAxisPosition(1, AXIS_POSITION_LEFT); + $data->setScatterSerie('Probe 1', 'Probe 3', 0); + $data->setScatterSerieDescription(0, 'This year'); + $data->setScatterSerieColor(0, ['R' => 0, 'G' => 0, 'B' => 0]); + $data->setScatterSerie('Probe 2', 'Probe 3', 1); + $data->setScatterSerieDescription(1, 'Last Year'); + $image = new Image(400, 400, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawScatterBestFit() - Linear regression', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(50, 60, 350, 360); + $scatterChart = new Scatter($image, $data); + $scatterChart->drawScatterScale(); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $scatterChart->drawScatterPlotChart(); + $scatterChart->drawScatterLegend(280, 380, ['Mode' => LEGEND_HORIZONTAL, 'Style' => LEGEND_NOBORDER]); + $scatterChart->drawScatterBestFit(); + + $filename = $this->tester->getOutputPathForChart('drawScatterBestFit.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function testLineChartRender() + { + $data = new Data(); + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(cos(deg2rad($i)) * 20, 'Probe 1'); + } + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(sin(deg2rad($i)) * 20, 'Probe 2'); + } + $data->setAxisName(0, 'Index'); + $data->setAxisXY(0, AXIS_X); + $data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, 'Probe 3'); + } + $data->setSerieOnAxis('Probe 3', 1); + $data->setAxisName(1, 'Degree'); + $data->setAxisXY(1, AXIS_Y); + $data->setAxisUnit(1, '°'); + $data->setAxisPosition(1, AXIS_POSITION_RIGHT); + $data->setScatterSerie('Probe 1', 'Probe 3', 0); + $data->setScatterSerieDescription(0, 'This year'); + $data->setScatterSerieTicks(0, 4); + $data->setScatterSerieColor(0, ['R' => 0, 'G' => 0, 'B' => 0]); + $data->setScatterSerie('Probe 2', 'Probe 3', 1); + $data->setScatterSerieDescription(1, 'Last Year'); + $image = new Image(400, 400, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawScatterLineChart() - Draw a scatter line chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(50, 50, 350, 350); + $scatterChart = new Scatter($image, $data); + $scatterChart->drawScatterScale(); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $scatterChart->drawScatterLineChart(); + $scatterChart->drawScatterLegend(280, 380, ['Mode' => LEGEND_HORIZONTAL, 'Style' => LEGEND_NOBORDER]); + + $filename = $this->tester->getOutputPathForChart('drawScatterLine.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function testPlotChartRender() + { + $data = new Data(); + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(cos(deg2rad($i)) * 20, 'Probe 1'); + } + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints(sin(deg2rad($i)) * 20, 'Probe 2'); + } + $data->setAxisName(0, 'Index'); + $data->setAxisXY(0, AXIS_X); + $data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + for ($i = 0; $i <= 360; $i = $i + 10) { + $data->addPoints($i, 'Probe 3'); + } + $data->setSerieOnAxis('Probe 3', 1); + $data->setAxisName(1, 'Degree'); + $data->setAxisXY(1, AXIS_Y); + $data->setAxisUnit(1, '°'); + $data->setAxisPosition(1, AXIS_POSITION_RIGHT); + $data->setScatterSerie('Probe 1', 'Probe 3', 0); + $data->setScatterSerieDescription(0, 'This year'); + $data->setScatterSerieColor(0, ['R' => 0, 'G' => 0, 'B' => 0]); + $data->setScatterSerie('Probe 2', 'Probe 3', 1); + $data->setScatterSerieDescription(1, 'Last Year'); + $data->setScatterSeriePicture(1, sprintf('%s/../_data/accept.png', __DIR__)); + $image = new Image(400, 400, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawScatterPlotChart() - Draw a scatter plot chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(50, 50, 350, 350); + $scatterChart = new Scatter($image, $data); + $scatterChart->drawScatterScale(); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $scatterChart->drawScatterPlotChart(); + $scatterChart->drawScatterLegend(260, 375, ['Mode' => LEGEND_HORIZONTAL, 'Style' => LEGEND_NOBORDER]); + + $filename = $this->tester->getOutputPathForChart('drawScatterPlot.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function testSplineChartRender() + { + $data = new Data(); + for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints(rand(1, 30), 'Probe 1'); + } + for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints(rand(1, 30), 'Probe 2'); + } + $data->setAxisName(0, 'Index'); + $data->setAxisXY(0, AXIS_X); + $data->setAxisPosition(0, AXIS_POSITION_BOTTOM); + for ($i = 0; $i <= 360; $i = $i + 90) { + $data->addPoints($i, 'Probe 3'); + } + $data->setSerieOnAxis('Probe 3', 1); + $data->setAxisName(1, 'Degree'); + $data->setAxisXY(1, AXIS_Y); + $data->setAxisUnit(1, '°'); + $data->setAxisPosition(1, AXIS_POSITION_RIGHT); + $data->setScatterSerie('Probe 1', 'Probe 3', 0); + $data->setScatterSerieDescription(0, 'This year'); + $data->setScatterSerieTicks(0, 4); + $data->setScatterSerieColor(0, ['R' => 0, 'G' => 0, 'B' => 0]); + $data->setScatterSerie('Probe 2', 'Probe 3', 1); + $data->setScatterSerieDescription(1, 'Last Year'); + $image = new Image(400, 400, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->setFontProperties(['FontName' => '../fonts/Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawScatterSplineChart() - Draw a scatter spline chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => '../fonts/pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(50, 50, 350, 350); + $scatterChart = new Scatter($image, $data); + $scatterChart->drawScatterScale(); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $scatterChart->drawScatterSplineChart(); + $scatterChart->drawScatterPlotChart(); + $scatterChart->drawScatterLegend( + 280, + 380, + ['Mode' => LEGEND_HORIZONTAL, 'Style' => LEGEND_NOBORDER] + ); + + $filename = $this->tester->getOutputPathForChart('drawScatterSpline.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/SplineTest.php b/vendor/szymach/c-pchart/tests/unit/SplineTest.php new file mode 100644 index 0000000..ce9c752 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/SplineTest.php @@ -0,0 +1,45 @@ +addPoints([], 'Serie1'); + + $image = new Image(700, 230, $data); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 20] + ); + $firstCoordinates = [[40, 80], [280, 60], [340, 166], [590, 120]]; + $fistSplineSettings = ['R' => 255, 'G' => 255, 'B' => 255, 'ShowControl' => true]; + $image->drawSpline($firstCoordinates, $fistSplineSettings); + $secondCoordinates = [[250, 50], [250, 180], [350, 180], [350, 50]]; + $secondSplineSettings = [ + 'R' => 255, + 'G' => 255, + 'B' => 255, + 'ShowControl' => true, + 'Ticks' => 4 + ]; + $image->drawSpline($secondCoordinates, $secondSplineSettings); + $filename = $this->tester->getOutputPathForChart('drawSpline.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/SplitPathTest.php b/vendor/szymach/c-pchart/tests/unit/SplitPathTest.php new file mode 100644 index 0000000..59b3c22 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/SplitPathTest.php @@ -0,0 +1,90 @@ + 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pSplit - Draw splitted path charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties( + ['FontName' => 'Forgotte.ttf', 'FontSize' => 10, 'R' => 80, 'G' => 80, 'B' => 80] + ); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $data = new Data(); + $data->addPoints([30, 20, 15, 10, 8, 4], 'Score'); + $data->addPoints( + ['End of visit', 'Home Page', 'Product Page', 'Sales', 'Statistics', 'Prints'], + 'Labels' + ); + $data->setAbscissa('Labels'); + $SplitChart = new Split(); + $settings = [ + 'TextPos' => TEXT_POS_RIGHT, + 'TextPadding' => 10, + 'Spacing' => 20, + 'Surrounding' => 40 + ]; + $image->setGraphArea(10, 20, 340, 230); + $SplitChart->drawSplitPath($image, $data, $settings); + $data2 = new Data(); + $data2->addPoints([30, 20, 15], 'Score'); + $data2->addPoints(['UK', 'FR', 'ES'], 'Labels'); + $data2->setAbscissa('Labels'); + $settings = ['TextPadding' => 4, 'Spacing' => 30, 'Surrounding' => 20]; + $image->setGraphArea(350, 50, 690, 200); + $SplitChart->drawSplitPath($image, $data2, $settings); + + $filename = $this->tester->getOutputPathForChart('drawSplit.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/SpringTest.php b/vendor/szymach/c-pchart/tests/unit/SpringTest.php new file mode 100644 index 0000000..b61a4ad --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/SpringTest.php @@ -0,0 +1,114 @@ +drawGradientArea( + 0, + 0, + 300, + 300, + DIRECTION_HORIZONTAL, + [ + 'StartR' => 217, 'StartG' => 250, 'StartB' => 116, 'EndR' => 181, 'EndG' => 209, + 'EndB' => 27, 'Alpha' => 100 + ] + ); + $image->drawGradientArea( + 0, + 0, + 300, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 299, 299, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pSpring - Draw spring charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setGraphArea(20, 20, 280, 280); + $image->setFontProperties( + ['FontName' => 'Forgotte.ttf', 'FontSize' => 9, 'R' => 80, 'G' => 80, 'B' => 80] + ); + $image->setShadow( + true, + ['X' => 2, 'Y' => 2, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $springChart = new Spring(); + $springChart->addNode( + 0, + ['Shape' => NODE_SHAPE_SQUARE, 'FreeZone' => 60, 'Size' => 20, 'NodeType' => NODE_TYPE_CENTRAL] + ); + $springChart->addNode(1, ['Connections' => '0']); + $springChart->addNode(2, ['Connections' => '0']); + $springChart->addNode(3, ['Shape' => NODE_SHAPE_TRIANGLE, 'Connections' => '1']); + $springChart->addNode(4, ['Shape' => NODE_SHAPE_TRIANGLE, 'Connections' => '1']); + $springChart->addNode(5, ['Shape' => NODE_SHAPE_TRIANGLE, 'Connections' => '1']); + $springChart->addNode(6, ['Connections' => '2']); + $springChart->addNode(7, ['Connections' => '2']); + $springChart->addNode(8, ['Connections' => '2']); + $springChart->setNodesColor( + 0, + [ + 'R' => 215, 'G' => 163, 'B' => 121, 'BorderR' => 166, 'BorderG' => 115, + 'BorderB' => 74 + ] + ); + $springChart->setNodesColor( + [1, 2], + ['R' => 150, 'G' => 215, 'B' => 121, 'Surrounding' => -30] + ); + $springChart->setNodesColor( + [3, 4, 5], + ['R' => 216, 'G' => 166, 'B' => 14, 'Surrounding' => -30] + ); + $springChart->setNodesColor( + [6, 7, 8], + ['R' => 179, 'G' => 121, 'B' => 215, 'Surrounding' => -30] + ); + $springChart->linkProperties( + 0, + 1, + ['R' => 255, 'G' => 0, 'B' => 0, 'Ticks' => 2] + ); + $springChart->linkProperties( + 0, + 2, + ['R' => 255, 'G' => 0, 'B' => 0, 'Ticks' => 2] + ); + $springChart->drawSpring($image); + + $filename = $this->tester->getOutputPathForChart('drawSpring.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/StackedAreaTest.php b/vendor/szymach/c-pchart/tests/unit/StackedAreaTest.php new file mode 100644 index 0000000..fac0c91 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/StackedAreaTest.php @@ -0,0 +1,77 @@ +addPoints([4, 0, 0, 12, 8, 3, 0, 12, 8], 'Frontend #1'); + $data->addPoints([3, 12, 15, 8, 5, 5, 12, 15, 8], 'Frontend #2'); + $data->addPoints([2, 7, 5, 18, 19, 22, 7, 5, 18], 'Frontend #3'); + $data->setAxisName(0, 'Average Usage'); + $data->addPoints( + ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jui', 'Aug', 'Sep'], + 'Labels' + ); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + $data->normalize(100, '%'); + $image = new Image(700, 230, $data); + $image->drawGradientArea( + 0, + 0, + 700, + 230, + DIRECTION_VERTICAL, + [ + 'StartR' => 240, 'StartG' => 240, 'StartB' => 240, 'EndR' => 180, 'EndG' => 180, + 'EndB' => 180, 'Alpha' => 100 + ] + ); + $image->drawGradientArea( + 0, + 0, + 700, + 230, + DIRECTION_HORIZONTAL, + [ + 'StartR' => 240, 'StartG' => 240, 'StartB' => 240, 'EndR' => 180, 'EndG' => 180, + 'EndB' => 180, 'Alpha' => 20 + ] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(60, 20, 680, 190); + $image->drawScale(['XMargin' => 2, 'DrawSubTicks' => true, 'Mode' => SCALE_MODE_ADDALL]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->drawStackedAreaChart(['Surrounding' => 60]); + $image->setShadow(false); + $image->drawLegend(480, 210, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawStackedArea.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/StackedBarTest.php b/vendor/szymach/c-pchart/tests/unit/StackedBarTest.php new file mode 100644 index 0000000..c6ee50e --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/StackedBarTest.php @@ -0,0 +1,85 @@ +addPoints( + [-7, -8, -15, -20, -18, -12, 8, -19, 9, 16, -20, 8, 10, -10, -14, -20, 8, -9, -19], + 'Probe 3' + ); + $data->addPoints( + [19, 0, -8, 8, -8, 12, -19, -10, 5, 12, -20, -8, 10, -11, -12, 8, -17, -14, 0], + 'Probe 4' + ); + $data->setAxisName(0, 'Temperatures'); + $data->addPoints( + [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], + 'Time' + ); + $data->setSerieDescription('Time', 'Hour of the day'); + $data->setAbscissa('Time'); + $data->setXAxisUnit('h'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(60, 30, 650, 190); + $image->drawScale([ + 'CycleBackground' => true, + 'DrawSubTicks' => true, + 'GridR' => 0, + 'GridG' => 0, + 'GridB' => 0, + 'GridAlpha' => 10, + 'Mode' => SCALE_MODE_ADDALL + ]); + $image->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $image->setShadow(false); + $image->drawThreshold(-40, ['WriteCaption' => true, 'R' => 0, 'G' => 0, 'B' => 0, 'Ticks' => 4]); + $image->drawThreshold(28, ['WriteCaption' => true, 'R' => 0, 'G' => 0, 'B' => 0, 'Ticks' => 4]); + $image->drawStackedBarChart([ + 'Rounded' => true, + 'DisplayValues' => true, + 'DisplayColor' => DISPLAY_AUTO, + 'DisplaySize' => 6, + 'BorderR' => 255, + 'BorderG' => 255, + 'BorderB' => 255 + ]); + $image->drawLegend(570, 212, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawStackedChart.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/StepTest.php b/vendor/szymach/c-pchart/tests/unit/StepTest.php new file mode 100644 index 0000000..dd31aa2 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/StepTest.php @@ -0,0 +1,112 @@ +addPoints([-4, VOID, VOID, 12, 8, 3], 'Probe 1'); + $data->addPoints([3, 12, 15, 8, 5, -5], 'Probe 2'); + $data->addPoints([2, 7, 5, 18, 19, 22], 'Probe 3'); + $data->setSerieTicks('Probe 2', 4); + $data->setAxisName(0, 'Temperatures'); + $data->addPoints(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 'Labels'); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 700, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 80 + ] + ); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'drawStepChart() - draw a step chart', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 250, + 55, + 'Average temperature', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setGraphArea(60, 60, 450, 190); + $image->drawFilledRectangle( + 60, + 60, + 450, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->drawStepChart(['DisplayValues' => true, 'DisplayColor' => DISPLAY_AUTO]); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $image->setShadow( + true, + ['X' => -1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10] + ); + $image->drawStepChart(); + $image->setShadow(false); + $image->drawLegend(510, 205, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawStep.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/StockTest.php b/vendor/szymach/c-pchart/tests/unit/StockTest.php new file mode 100644 index 0000000..6a38067 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/StockTest.php @@ -0,0 +1,91 @@ +addPoints([34, 55, 15, 62, 38, 42], 'Open'); + $data->addPoints([42, 25, 40, 38, 49, 36], 'Close'); + $data->addPoints([27, 14, 12, 25, 32, 32], 'Min'); + $data->addPoints([45, 59, 47, 65, 64, 48], 'Max'); + $data->setAxisDisplay(0, AXIS_FORMAT_CURRENCY, '$'); + $data->addPoints(['8h', '10h', '12h', '14h', '16h', '18h'], 'Time'); + $data->setAbscissa('Time'); + $image = new Image(700, 230, $data); + $settings = [ + 'R' => 170, 'G' => 183, 'B' => 87, 'Dash' => 1, 'DashR' => 190, + 'DashG' => 203, 'DashB' => 107 + ]; + $image->drawFilledRectangle(0, 0, 700, 230, $settings); + $settings = [ + 'StartR' => 219, 'StartG' => 231, 'StartB' => 139, 'EndR' => 1, + 'EndG' => 138, 'EndB' => 68, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 700, 230, DIRECTION_VERTICAL, $settings); + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => '../fonts/Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 60, + 45, + 'Stock price', + ['FontSize' => 28, 'Align' => TEXT_ALIGN_BOTTOMLEFT] + ); + $image->setGraphArea(60, 60, 450, 190); + $image->drawFilledRectangle( + 60, + 60, + 450, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['DrawSubTicks' => true, 'CycleBackground' => true]); + $stockChart = new Stock($image, $data); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 30] + ); + $stockChart->drawStockChart(); + $data->setAxisDisplay(0, AXIS_FORMAT_DEFAULT); + $image->setShadow(false); + $image->setGraphArea(500, 60, 670, 190); + $image->drawFilledRectangle( + 500, + 60, + 670, + 190, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 10] + ); + $image->drawScale(['Pos' => SCALE_POS_TOPBOTTOM, 'DrawSubTicks' => true]); + $stockChart = new Stock($image, $data); + $image->setShadow( + true, + ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 30] + ); + $stockChart->drawStockChart(); + $filename = $this->tester->getOutputPathForChart('drawStock.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/SurfaceTest.php b/vendor/szymach/c-pchart/tests/unit/SurfaceTest.php new file mode 100644 index 0000000..f47ebdd --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/SurfaceTest.php @@ -0,0 +1,171 @@ + 179, 'G' => 217, 'B' => 91, 'Dash' => 1, 'DashR' => 199, + 'DashG' => 237, 'DashB' => 111 + ]; + $image->drawFilledRectangle(0, 0, 400, 400, $settings); + $settings = [ + 'StartR' => 194, 'StartG' => 231, 'StartB' => 44, 'EndR' => 43, + 'EndG' => 107, 'EndB' => 58, 'Alpha' => 50 + ]; + $image->drawGradientArea(0, 0, 400, 400, DIRECTION_VERTICAL, $settings); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, 'StartG' => 0, 'StartB' => 0, 'EndR' => 50, 'EndG' => 50, + 'EndB' => 50, 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pSurface() :: 2D surface charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setGraphArea(20, 40, 380, 380); + $image->drawFilledRectangle( + 20, + 40, + 380, + 380, + ['R' => 255, 'G' => 255, 'B' => 255, 'Surrounding' => -200, 'Alpha' => 20] + ); + $image->setShadow(true, ['X' => 1, 'Y' => 1]); + $surfaceChart = new Surface($image); + $surfaceChart->setGrid(20, 20); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $surfaceChart->writeXLabels(); + $surfaceChart->writeYLabels(); + for ($i = 0; $i <= 50; $i++) { + $surfaceChart->addPoint(rand(0, 20), rand(0, 20), rand(0, 100)); + } + $surfaceChart->computeMissing(); + $surfaceChart->drawSurface(['Border' => true, 'Surrounding' => 40]); + + $filename = $this->tester->getOutputPathForChart('drawSurface.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } + + public function testContourChartRender() + { + $image = new Image(400, 400); + $image->drawFilledRectangle( + 0, + 0, + 400, + 400, + [ + 'R' => 179, + 'G' => 217, + 'B' => 91, + 'Dash' => 1, + 'DashR' => 199, + 'DashG' => 237, + 'DashB' => 111 + ] + ); + $image->drawGradientArea( + 0, + 0, + 400, + 400, + DIRECTION_VERTICAL, + [ + 'StartR' => 194, + 'StartG' => 231, + 'StartB' => 44, + 'EndR' => 43, + 'EndG' => 107, + 'EndB' => 58, + 'Alpha' => 50 + ] + ); + $image->drawGradientArea( + 0, + 0, + 400, + 20, + DIRECTION_VERTICAL, + [ + 'StartR' => 0, + 'StartG' => 0, + 'StartB' => 0, + 'EndR' => 50, + 'EndG' => 50, + 'EndB' => 50, + 'Alpha' => 100 + ] + ); + $image->drawRectangle(0, 0, 399, 399, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Silkscreen.ttf', 'FontSize' => 6]); + $image->drawText( + 10, + 13, + 'pSurface() :: 2D surface charts', + ['R' => 255, 'G' => 255, 'B' => 255] + ); + $image->setGraphArea(20, 40, 380, 380); + $image->drawFilledRectangle( + 20, + 40, + 380, + 380, + [ + 'R' => 255, + 'G' => 255, + 'B' => 255, + 'Surrounding' => -200, + 'Alpha' => 20 + ] + ); + + $image->setShadow(true, ['X' => 1, 'Y' => 1]); + $surfaceChart = new Surface($image); + $surfaceChart->setGrid(20, 20); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $surfaceChart->writeXLabels(['Position' => LABEL_POSITION_BOTTOM]); + $surfaceChart->writeYLabels(); + for ($i = 0; $i <= 50; $i++) { + $surfaceChart->addPoint(rand(0, 20), rand(0, 20), rand(0, 100)); + } + $surfaceChart->computeMissing(); + $surfaceChart->drawSurface(['Border' => true, 'Surrounding' => 40]); + $surfaceChart->drawContour(50, ['R' => 0, 'G' => 0, 'B' => 0]); + + $filename = $this->tester->getOutputPathForChart('drawContour.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +} diff --git a/vendor/szymach/c-pchart/tests/unit/ZoneTest.php b/vendor/szymach/c-pchart/tests/unit/ZoneTest.php new file mode 100644 index 0000000..ed726bc --- /dev/null +++ b/vendor/szymach/c-pchart/tests/unit/ZoneTest.php @@ -0,0 +1,64 @@ +addPoints(log($i + 1) * 10, 'Bounds 1'); + $data->addPoints(log($i + 3) * 10 + rand(0, 2) - 1, 'Probe'); + $data->addPoints(log($i + 6) * 10, 'Bounds 2'); + $data->addPoints($i * 10, 'Labels'); + } + $data->setAxisName(0, 'Size (cm)'); + $data->setSerieDescription('Labels', 'Months'); + $data->setAbscissa('Labels'); + $data->setAbscissaName('Time (years)'); + $image = new Image(700, 230, $data); + $image->Antialias = false; + $image->drawRectangle(0, 0, 699, 229, ['R' => 0, 'G' => 0, 'B' => 0]); + $image->setFontProperties(['FontName' => 'Forgotte.ttf', 'FontSize' => 11]); + $image->drawText( + 150, + 35, + 'Size by time generations', + ['FontSize' => 20, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE] + ); + $image->setFontProperties(['FontName' => 'pf_arma_five.ttf', 'FontSize' => 6]); + $image->setGraphArea(40, 40, 680, 200); + $scaleSettings = [ + 'LabelSkip' => 4, 'XMargin' => 10, 'YMargin' => 10, 'Floating' => true, + 'GridR' => 200, 'GridG' => 200, 'GridB' => 200, 'DrawSubTicks' => true, + 'CycleBackground' => true + ]; + $image->drawScale($scaleSettings); + $image->Antialias = true; + $image->drawZoneChart('Bounds 1', 'Bounds 2'); + $data->setSerieDrawable(['Bounds 1', 'Bounds 2'], false); + $image->drawStepChart(); + $image->drawLegend(640, 20, ['Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL]); + + $filename = $this->tester->getOutputPathForChart('drawZone.png'); + $image->render($filename); + $image->stroke(); + + $this->tester->seeFileFound($filename); + } +}

K`* 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 literal 0 HcmV?d00001 diff --git a/vendor/szymach/c-pchart/resources/fonts/verdana.ttf b/vendor/szymach/c-pchart/resources/fonts/verdana.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5a059d23c476d9d68d190136ec8cfccd5221310d GIT binary patch literal 189144 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%0D`usD|`Qc4B$QCB_3T&hD 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