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);
+ }
+}