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 0000000..d115733 Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/Bedizen.ttf differ 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 0000000..a2f7f4a Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/Forgotte.ttf differ 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 0000000..055932a Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/GeosansLight.ttf differ diff --git a/vendor/szymach/c-pchart/resources/fonts/MankSans.ttf b/vendor/szymach/c-pchart/resources/fonts/MankSans.ttf new file mode 100644 index 0000000..a6146a9 Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/MankSans.ttf differ 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 0000000..ae4425d Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/Silkscreen.ttf differ 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 0000000..514030a Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/advent_light.ttf differ 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 0000000..8b6e3c9 Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/calibri.ttf differ diff --git a/vendor/szymach/c-pchart/resources/fonts/pf_arma_five.ttf b/vendor/szymach/c-pchart/resources/fonts/pf_arma_five.ttf new file mode 100644 index 0000000..db04ec3 Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/pf_arma_five.ttf differ 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 0000000..5a059d2 Binary files /dev/null and b/vendor/szymach/c-pchart/resources/fonts/verdana.ttf differ diff --git a/vendor/szymach/c-pchart/resources/palettes/autumn.color b/vendor/szymach/c-pchart/resources/palettes/autumn.color new file mode 100644 index 0000000..032b020 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/autumn.color @@ -0,0 +1,6 @@ +185,106,154,100 +216,137,184,100 +156,192,137,100 +216,243,201,100 +253,232,215,100 +255,255,255,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/blind.color b/vendor/szymach/c-pchart/resources/palettes/blind.color new file mode 100644 index 0000000..94355e2 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/blind.color @@ -0,0 +1,6 @@ +109,152,171,100 +0,39,94,100 +254,183,41,100 +168,177,184,100 +255,255,255,100 +0,0,0,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/evening.color b/vendor/szymach/c-pchart/resources/palettes/evening.color new file mode 100644 index 0000000..8c9b71a --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/evening.color @@ -0,0 +1,6 @@ +242,245,237,100 +255,194,0,100 +255,91,0,100 +184,0,40,100 +132,0,46,100 +74,192,242,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/kitchen.color b/vendor/szymach/c-pchart/resources/palettes/kitchen.color new file mode 100644 index 0000000..c60d3b6 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/kitchen.color @@ -0,0 +1,6 @@ +155,225,251,100 +197,239,253,100 +189,32,49,100 +35,31,32,100 +255,255,255,100 +0,98,149,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/light.color b/vendor/szymach/c-pchart/resources/palettes/light.color new file mode 100644 index 0000000..08954f0 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/light.color @@ -0,0 +1,7 @@ +239,210,121,100 +149,203,233,100 +2,71,105,100 +175,215,117,100 +44,87,0,100 +222,157,127,100 + diff --git a/vendor/szymach/c-pchart/resources/palettes/navy.color b/vendor/szymach/c-pchart/resources/palettes/navy.color new file mode 100644 index 0000000..cede58c --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/navy.color @@ -0,0 +1,6 @@ +25,78,132,100 +59,107,156,100 +31,36,42,100 +55,65,74,100 +96,187,34,100 +242,186,187,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/shade.color b/vendor/szymach/c-pchart/resources/palettes/shade.color new file mode 100644 index 0000000..ba9bcd5 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/shade.color @@ -0,0 +1,6 @@ +117,113,22,100 +174,188,33,100 +217,219,86,100 +0,71,127,100 +76,136,190,100 +141,195,233,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/spring.color b/vendor/szymach/c-pchart/resources/palettes/spring.color new file mode 100644 index 0000000..d7af18b --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/spring.color @@ -0,0 +1,6 @@ +146,123,81,100 +168,145,102,100 +128,195,28,100 +188,221,90,100 +255,121,0,100 +251,179,107,100 diff --git a/vendor/szymach/c-pchart/resources/palettes/summer.color b/vendor/szymach/c-pchart/resources/palettes/summer.color new file mode 100644 index 0000000..f7aacc7 --- /dev/null +++ b/vendor/szymach/c-pchart/resources/palettes/summer.color @@ -0,0 +1,6 @@ +253,184,19,100 +246,139,31,100 +241,112,34,100 +98,194,204,100 +228,246,248,100 +238,246,108,100 diff --git a/vendor/szymach/c-pchart/src/Barcode/Barcode128.php b/vendor/szymach/c-pchart/src/Barcode/Barcode128.php new file mode 100644 index 0000000..6bce999 --- /dev/null +++ b/vendor/szymach/c-pchart/src/Barcode/Barcode128.php @@ -0,0 +1,274 @@ +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 0000000..89c8129 Binary files /dev/null and b/vendor/szymach/c-pchart/tests/_data/accept.png differ diff --git a/vendor/szymach/c-pchart/tests/_data/test_palette.txt b/vendor/szymach/c-pchart/tests/_data/test_palette.txt new file mode 100644 index 0000000..d6d5d07 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/_data/test_palette.txt @@ -0,0 +1,11 @@ +244, 91, 91, 100 +128, 133, 233, 100 +141, 70, 84, 100 +119, 152, 191, 100 +170, 238, 238, 100 +255, 0, 102, 100 +238, 170, 238, 100 +85, 191, 59, 100 +223, 83, 83, 100 +119, 152, 191, 100 +170, 238, 238, 100 diff --git a/vendor/szymach/c-pchart/tests/_output/.gitignore b/vendor/szymach/c-pchart/tests/_output/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/vendor/szymach/c-pchart/tests/_output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/vendor/szymach/c-pchart/tests/_support/Helper/Unit.php b/vendor/szymach/c-pchart/tests/_support/Helper/Unit.php new file mode 100644 index 0000000..8eed6fb --- /dev/null +++ b/vendor/szymach/c-pchart/tests/_support/Helper/Unit.php @@ -0,0 +1,42 @@ +getChartDirectoryPath(); + 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); + } +}