From e26f7d2b1dcaeb809dff7ae2a183ff8eac896baf Mon Sep 17 00:00:00 2001 From: Paul Richards Date: Sat, 26 Sep 2009 16:04:34 +0100 Subject: [PATCH] sync ecz libraries to latest release --- library/README.libs | 2 +- library/ezc/Base/src/base.php | 1312 ++--- library/ezc/Base/src/exceptions/autoload.php | 76 +- .../double_class_repository_prefix.php | 68 +- library/ezc/Base/src/exceptions/exception.php | 86 +- .../src/exceptions/extension_not_found.php | 76 +- .../Base/src/exceptions/file_exception.php | 50 +- library/ezc/Base/src/exceptions/file_io.php | 100 +- .../Base/src/exceptions/file_not_found.php | 86 +- .../Base/src/exceptions/file_permission.php | 126 +- .../functionality_not_supported.php | 62 +- .../exceptions/init_callback_configured.php | 62 +- .../src/exceptions/invalid_callback_class.php | 62 +- .../src/exceptions/invalid_parent_class.php | 58 +- .../src/exceptions/property_not_found.php | 60 +- .../src/exceptions/property_permission.php | 84 +- .../Base/src/exceptions/setting_not_found.php | 58 +- .../ezc/Base/src/exceptions/setting_value.php | 84 +- library/ezc/Base/src/exceptions/value.php | 86 +- library/ezc/Base/src/exceptions/whatever.php | 80 +- library/ezc/Base/src/ezc_bootstrap.php | 80 +- library/ezc/Base/src/features.php | 716 +-- library/ezc/Base/src/file.php | 990 ++-- library/ezc/Base/src/init.php | 250 +- .../interfaces/configuration_initializer.php | 64 +- .../ezc/Base/src/interfaces/persistable.php | 80 +- library/ezc/Base/src/metadata.php | 233 +- library/ezc/Base/src/metadata/pear.php | 251 +- library/ezc/Base/src/metadata/tarball.php | 306 +- library/ezc/Base/src/options.php | 348 +- library/ezc/Base/src/options/autoload.php | 150 +- library/ezc/Base/src/struct.php | 84 +- .../Base/src/structs/file_find_context.php | 144 +- .../Base/src/structs/repository_directory.php | 166 +- library/ezc/Graph/src/axis/container.php | 442 +- library/ezc/Graph/src/axis/date.php | 1266 ++--- library/ezc/Graph/src/axis/labeled.php | 1040 ++-- library/ezc/Graph/src/axis/logarithmic.php | 682 +-- library/ezc/Graph/src/axis/numeric.php | 1002 ++-- library/ezc/Graph/src/charts/bar.php | 188 +- library/ezc/Graph/src/charts/line.php | 1430 ++--- library/ezc/Graph/src/charts/odometer.php | 592 +- library/ezc/Graph/src/charts/pie.php | 616 +-- library/ezc/Graph/src/charts/radar.php | 914 ++-- library/ezc/Graph/src/colors/color.php | 580 +- .../ezc/Graph/src/colors/linear_gradient.php | 294 +- .../ezc/Graph/src/colors/radial_gradient.php | 346 +- library/ezc/Graph/src/data_container/base.php | 450 +- .../ezc/Graph/src/data_container/single.php | 102 +- library/ezc/Graph/src/datasets/array.php | 142 +- library/ezc/Graph/src/datasets/average.php | 718 +-- library/ezc/Graph/src/datasets/base.php | 604 +- library/ezc/Graph/src/datasets/numeric.php | 574 +- .../ezc/Graph/src/datasets/property/axis.php | 124 +- .../Graph/src/datasets/property/boolean.php | 74 +- .../ezc/Graph/src/datasets/property/color.php | 74 +- .../Graph/src/datasets/property/integer.php | 74 +- .../Graph/src/datasets/property/string.php | 74 +- library/ezc/Graph/src/driver/cairo.php | 2020 +++---- library/ezc/Graph/src/driver/flash.php | 1944 +++---- library/ezc/Graph/src/driver/gd.php | 2472 ++++----- library/ezc/Graph/src/driver/svg.php | 2460 ++++----- library/ezc/Graph/src/driver/svg_font.php | 4 +- library/ezc/Graph/src/driver/verbose.php | 484 +- library/ezc/Graph/src/element/axis.php | 1070 ++-- library/ezc/Graph/src/element/background.php | 442 +- library/ezc/Graph/src/element/legend.php | 678 +-- library/ezc/Graph/src/element/text.php | 300 +- .../ezc/Graph/src/exceptions/date_parsing.php | 66 +- .../ezc/Graph/src/exceptions/exception.php | 40 +- .../src/exceptions/flash_bitmap_type.php | 62 +- .../Graph/src/exceptions/font_rendering.php | 80 +- .../ezc/Graph/src/exceptions/font_type.php | 64 +- .../src/exceptions/incompatible_driver.php | 68 +- .../src/exceptions/invalid_assignement.php | 62 +- .../ezc/Graph/src/exceptions/invalid_data.php | 64 +- .../src/exceptions/invalid_data_source.php | 66 +- .../src/exceptions/invalid_dimensions.php | 70 +- .../src/exceptions/invalid_display_type.php | 92 +- .../ezc/Graph/src/exceptions/invalid_id.php | 64 +- .../src/exceptions/invalid_image_file.php | 62 +- .../ezc/Graph/src/exceptions/invalid_keys.php | 62 +- .../src/exceptions/invalid_step_size.php | 64 +- library/ezc/Graph/src/exceptions/no_data.php | 60 +- .../ezc/Graph/src/exceptions/no_such_data.php | 62 +- .../Graph/src/exceptions/no_such_dataset.php | 62 +- .../Graph/src/exceptions/no_such_element.php | 62 +- .../ezc/Graph/src/exceptions/not_rendered.php | 64 +- .../Graph/src/exceptions/out_of_boundings.php | 70 +- .../out_of_logarithmical_boundings.php | 64 +- .../src/exceptions/reducement_failed.php | 62 +- .../src/exceptions/too_many_datasets.php | 62 +- .../exceptions/unknown_color_definition.php | 64 +- .../Graph/src/exceptions/unregular_steps.php | 62 +- .../src/exceptions/unsupported_image_type.php | 122 +- library/ezc/Graph/src/graph.php | 294 +- .../src/interfaces/axis_label_renderer.php | 1114 ++-- library/ezc/Graph/src/interfaces/chart.php | 592 +- .../Graph/src/interfaces/dataset_property.php | 418 +- library/ezc/Graph/src/interfaces/driver.php | 1480 ++--- library/ezc/Graph/src/interfaces/element.php | 684 +-- .../src/interfaces/odometer_renderer.php | 102 +- library/ezc/Graph/src/interfaces/palette.php | 568 +- .../Graph/src/interfaces/radar_renderer.php | 108 +- library/ezc/Graph/src/interfaces/renderer.php | 1434 ++--- .../src/interfaces/stacked_bar_renderer.php | 92 +- library/ezc/Graph/src/math/boundings.php | 218 +- library/ezc/Graph/src/math/matrix.php | 1022 ++-- library/ezc/Graph/src/math/polynom.php | 518 +- library/ezc/Graph/src/math/rotation.php | 198 +- library/ezc/Graph/src/math/transformation.php | 192 +- library/ezc/Graph/src/math/translation.php | 94 +- library/ezc/Graph/src/math/vector.php | 388 +- .../ezc/Graph/src/options/cairo_driver.php | 200 +- library/ezc/Graph/src/options/chart.php | 214 +- library/ezc/Graph/src/options/driver.php | 344 +- .../ezc/Graph/src/options/flash_driver.php | 214 +- library/ezc/Graph/src/options/font.php | 620 +-- library/ezc/Graph/src/options/gd_driver.php | 362 +- library/ezc/Graph/src/options/line_chart.php | 418 +- .../ezc/Graph/src/options/odometer_chart.php | 226 +- library/ezc/Graph/src/options/pie_chart.php | 302 +- library/ezc/Graph/src/options/radar_chart.php | 344 +- library/ezc/Graph/src/options/renderer.php | 496 +- library/ezc/Graph/src/options/renderer_2d.php | 240 +- library/ezc/Graph/src/options/renderer_3d.php | 378 +- library/ezc/Graph/src/options/svg_driver.php | 544 +- library/ezc/Graph/src/palette/black.php | 228 +- library/ezc/Graph/src/palette/ez.php | 194 +- library/ezc/Graph/src/palette/ez_blue.php | 180 +- library/ezc/Graph/src/palette/ez_green.php | 180 +- library/ezc/Graph/src/palette/ez_red.php | 180 +- library/ezc/Graph/src/palette/tango.php | 174 +- library/ezc/Graph/src/renderer/2d.php | 3770 ++++++------- library/ezc/Graph/src/renderer/3d.php | 4848 ++++++++--------- .../Graph/src/renderer/axis_label_boxed.php | 436 +- .../src/renderer/axis_label_centered.php | 532 +- .../Graph/src/renderer/axis_label_exact.php | 608 +-- .../Graph/src/renderer/axis_label_none.php | 88 +- .../Graph/src/renderer/axis_label_radar.php | 644 +-- .../Graph/src/renderer/axis_label_rotated.php | 866 +-- library/ezc/Graph/src/structs/context.php | 184 +- library/ezc/Graph/src/structs/coordinate.php | 152 +- library/ezc/Graph/src/structs/step.php | 212 +- library/ezc/Graph/src/tools.php | 366 +- library/ezc/autoload/base_autoload.php | 7 +- library/ezc/autoload/graph_autoload.php | 4 +- 147 files changed, 30462 insertions(+), 30427 deletions(-) diff --git a/library/README.libs b/library/README.libs index 09a4221a3b..41a025d182 100644 --- a/library/README.libs +++ b/library/README.libs @@ -6,7 +6,7 @@ directory | project | version | status ----------------------------------------------------------------- adodb | adodb | 5.0.9a | patched: various, see git disposable | disposable | 1.1.0 | unpatched -ezc | ez Components | 2008.2.3 | unpatched +ezc | ez Components | 2009.1.2 | unpatched nusoap | nusoap | 0.7.3 | unpatched phpmailer | PHPMailer | 5.0.2 | unpatched projax | projax | | unpatched diff --git a/library/ezc/Base/src/base.php b/library/ezc/Base/src/base.php index aae882fd11..4907f55e5f 100644 --- a/library/ezc/Base/src/base.php +++ b/library/ezc/Base/src/base.php @@ -1,656 +1,656 @@ -array) - */ - protected static $repositoryDirs = array(); - - /** - * This variable stores all the elements from the autoload arrays. When a - * new autoload file is loaded, their files are added to this array. - * - * @var array(string=>string) - */ - protected static $autoloadArray = array(); - - /** - * This variable stores all the elements from the autoload arrays for - * external repositories. When a new autoload file is loaded, their files - * are added to this array. - * - * @var array(string=>string) - */ - protected static $externalAutoloadArray = array(); - - /** - * Options for the ezcBase class. - * - * @var ezcBaseOptions - */ - static private $options; - - /** - * Associates an option object with this static class. - * - * @param ezcBaseAutoloadOptions $options - */ - static public function setOptions( ezcBaseAutoloadOptions $options ) - { - self::$options = $options; - } - - /** - * Tries to autoload the given className. If the className could be found - * this method returns true, otherwise false. - * - * This class caches the requested class names (including the ones who - * failed to load). - * - * @param string $className The name of the class that should be loaded. - * - * @return bool - */ - public static function autoload( $className ) - { - ezcBase::setPackageDir(); - - // Check whether the classname is already in the cached autoloadArray. - if ( array_key_exists( $className, ezcBase::$autoloadArray ) ) - { - // Is it registered as 'unloadable'? - if ( ezcBase::$autoloadArray[$className] == false ) - { - return false; - } - ezcBase::loadFile( ezcBase::$autoloadArray[$className] ); - - return true; - } - - // Check whether the classname is already in the cached autoloadArray - // for external repositories. - if ( array_key_exists( $className, ezcBase::$externalAutoloadArray ) ) - { - // Is it registered as 'unloadable'? - if ( ezcBase::$externalAutoloadArray[$className] == false ) - { - return false; - } - ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] ); - - return true; - } - - // Not cached, so load the autoload from the package. - // Matches the first and optionally the second 'word' from the classname. - $fileNames = array(); - if ( preg_match( "/^([a-z0-9]*)([A-Z][a-z0-9]*)([A-Z][a-z0-9]*)?/", $className, $matches ) !== false ) - { - $autoloadFile = ""; - // Try to match with both names, if available. - switch ( sizeof( $matches ) ) - { - case 4: - // check for x_y_autoload.php - $autoloadFile = strtolower( "{$matches[2]}_{$matches[3]}_autoload.php" ); - $fileNames[] = $autoloadFile; - if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) - { - return true; - } - // break intentionally missing. - - case 3: - // check for x_autoload.php - $autoloadFile = strtolower( "{$matches[2]}_autoload.php" ); - $fileNames[] = $autoloadFile; - if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) - { - return true; - } - - // check for autoload.php - $autoloadFile = 'autoload.php'; - $fileNames[] = $autoloadFile; - if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) - { - return true; - } - break; - } - - // Maybe there is another autoload available. - // Register this classname as false. - ezcBase::$autoloadArray[$className] = false; - } - - $path = ezcBase::$packageDir . 'autoload/'; - $realPath = realpath( $path ); - - if ( $realPath == '' ) - { - // Can not be tested, because if this happens, then the autoload - // environment has not been set-up correctly. - trigger_error( "Couldn't find autoload directory '$path'", E_USER_ERROR ); - } - - $dirs = self::getRepositoryDirectories(); - if ( ezcBase::$options && ezcBase::$options->debug ) - { - throw new ezcBaseAutoloadException( $className, $fileNames, $dirs ); - } - - return false; - } - - /** - * Sets the current working directory to $directory. - * - * @param string $directory - */ - public static function setWorkingDirectory( $directory ) - { - self::$libraryMode = 'custom'; - self::$currentWorkingDirectory = $directory; - } - - /** - * Figures out the base path of the eZ Components installation. - * - * It stores the path that it finds in a static member variable. The path - * depends on the installation method of the eZ Components. The SVN version - * has a different path than the PEAR installed version. - */ - protected static function setPackageDir() - { - if ( ezcBase::$packageDir !== null ) - { - return; - } - - // Get the path to the components. - $baseDir = dirname( __FILE__ ); - - switch ( ezcBase::$libraryMode ) - { - case "custom": - ezcBase::$packageDir = self::$currentWorkingDirectory . '/'; - break; - case "devel": - case "tarball": - ezcBase::$packageDir = $baseDir. "/../../"; - break; - case "pear"; - ezcBase::$packageDir = $baseDir. "/../"; - break; - } - } - - /** - * Tries to load the autoload array and, if loaded correctly, includes the class. - * - * @param string $fileName Name of the autoload file. - * @param string $className Name of the class that should be autoloaded. - * @param string $prefix The prefix of the class repository. - * - * @return bool True is returned when the file is correctly loaded. - * Otherwise false is returned. - */ - protected static function requireFile( $fileName, $className, $prefix ) - { - $autoloadDir = ezcBase::$packageDir . "autoload/"; - - // We need the full path to the fileName. The method file_exists() doesn't - // automatically check the (php.ini) library paths. Therefore: - // file_exists( "ezc/autoload/$fileName" ) doesn't work. - if ( $prefix === 'ezc' && file_exists( "$autoloadDir$fileName" ) ) - { - $array = require( "$autoloadDir$fileName" ); - - if ( is_array( $array) && array_key_exists( $className, $array ) ) - { - // Add the array to the cache, and include the requested file. - ezcBase::$autoloadArray = array_merge( ezcBase::$autoloadArray, $array ); - if ( ezcBase::$options !== null && ezcBase::$options->preload && !preg_match( '/Exception$/', $className ) ) - { - foreach ( $array as $loadClassName => $file ) - { - if ( $loadClassName !== 'ezcBase' && !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false ) && !preg_match( '/Exception$/', $loadClassName ) /*&& !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false )*/ ) - { - ezcBase::loadFile( ezcBase::$autoloadArray[$loadClassName] ); - } - } - } - else - { - ezcBase::loadFile( ezcBase::$autoloadArray[$className] ); - } - return true; - } - } - - // It is not in components autoload/ dir. - // try to search in additional dirs. - foreach ( ezcBase::$repositoryDirs as $repositoryPrefix => $extraDir ) - { - if ( gettype( $repositoryPrefix ) === 'string' && $repositoryPrefix !== $prefix ) - { - continue; - } - - if ( file_exists( $extraDir['autoloadDirPath'] . '/' . $fileName ) ) - { - $array = array(); - $originalArray = require( $extraDir['autoloadDirPath'] . '/' . $fileName ); - - // Building paths. - // Resulting path to class definition file consists of: - // path to extra directory with autoload file + - // basePath provided for current extra directory + - // path to class definition file stored in autoload file. - foreach ( $originalArray as $class => $classPath ) - { - $array[$class] = $extraDir['basePath'] . '/' . $classPath; - } - - if ( is_array( $array ) && array_key_exists( $className, $array ) ) - { - // Add the array to the cache, and include the requested file. - ezcBase::$externalAutoloadArray = array_merge( ezcBase::$externalAutoloadArray, $array ); - ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] ); - return true; - } - } - } - - // Nothing found :-(. - return false; - } - - /** - * Loads, require(), the given file name. If we are in development mode, - * "/src/" is inserted into the path. - * - * @param string $file The name of the file that should be loaded. - */ - protected static function loadFile( $file ) - { - switch ( ezcBase::$libraryMode ) - { - case "devel": - case "tarball": - list( $first, $second ) = explode( '/', $file, 2 ); - $file = $first . "/src/" . $second; - break; - - case "custom": - list( $first, $second ) = explode( '/', $file, 2 ); - // Add the "src/" after the package name. - if ( $first == 'Base' || $first == 'UnitTest' ) - { - list( $first, $second ) = explode( '/', $file, 2 ); - $file = $first . "/src/" . $second; - } - else - { - list( $first, $second, $third ) = explode( '/', $file, 3 ); - $file = $first . '/' . $second . "/src/" . $third; - } - break; - - case "pear": - /* do nothing, it's already correct */ - break; - } - - if ( file_exists( ezcBase::$packageDir . $file ) ) - { - require( ezcBase::$packageDir . $file ); - } - else - { - // Can not be tested, because if this happens, then one of the - // components has a broken autoload file. - throw new ezcBaseFileNotFoundException( ezcBase::$packageDir.$file ); - } - } - - /** - * Loads, require(), the given file name from an external package. - * - * @param string $file The name of the file that should be loaded. - */ - protected static function loadExternalFile( $file ) - { - if ( file_exists( $file ) ) - { - require( $file ); - } - else - { - throw new ezcBaseFileNotFoundException( $file ); - } - } - - /** - * Checks for dependencies on PHP versions or extensions - * - * The function as called by the $component component checks for the $type - * dependency. The dependency $type is compared against the $value. The - * function aborts the script if the dependency is not matched. - * - * @param string $component - * @param int $type - * @param mixed $value - */ - public static function checkDependency( $component, $type, $value ) - { - switch ( $type ) - { - case self::DEP_PHP_EXTENSION: - if ( extension_loaded( $value ) ) - { - return; - } - else - { - // Can not be tested as it would abort the PHP script. - die( "\nThe {$component} component depends on the default PHP extension '{$value}', which is not loaded.\n" ); - } - break; - - case self::DEP_PHP_VERSION: - $phpVersion = phpversion(); - if ( version_compare( $phpVersion, $value, '>=' ) ) - { - return; - } - else - { - // Can not be tested as it would abort the PHP script. - die( "\nThe {$component} component depends on the PHP version '{$value}', but the current version is '{$phpVersion}'.\n" ); - } - break; - } - } - - /** - * Return the list of directories that contain class repositories. - * - * The path to the eZ components directory is always included in the result - * array. Each element in the returned array has the format of: - * packageDirectory => ezcBaseRepositoryDirectory - * - * @return array(string=>ezcBaseRepositoryDirectory) - */ - public static function getRepositoryDirectories() - { - $autoloadDirs = array(); - ezcBase::setPackageDir(); - $repositoryDir = self::$currentWorkingDirectory ? self::$currentWorkingDirectory : ( realpath( dirname( __FILE__ ) . '/../../' ) ); - $autoloadDirs['ezc'] = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_INTERNAL, $repositoryDir, $repositoryDir . "/autoload" ); - - foreach ( ezcBase::$repositoryDirs as $extraDirKey => $extraDirArray ) - { - $repositoryDirectory = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_EXTERNAL, realpath( $extraDirArray['basePath'] ), realpath( $extraDirArray['autoloadDirPath'] ) ); - $autoloadDirs[$extraDirKey] = $repositoryDirectory; - } - - return $autoloadDirs; - } - - /** - * Adds an additional class repository. - * - * Used for adding class repositoryies outside the eZ components to be - * loaded by the autoload system. - * - * This function takes two arguments: $basePath is the base path for the - * whole class repository and $autoloadDirPath the path where autoload - * files for this repository are found. The paths in the autoload files are - * relative to the package directory as specified by the $basePath - * argument. I.e. class definition file will be searched at location - * $basePath + path to the class definition file as stored in the autoload - * file. - * - * addClassRepository() should be called somewhere in code before external classes - * are used. - * - * Example: - * Take the following facts: - * - * - * In this case you would need to create the following files in - * "./repos/autoloads". Please note that the part before _autoload.php in - * the filename is the first part of the classname, not considering - * the all lower-case letter prefix. - * - * "my_autoload.php": - * - * 'Me/myclass1.php', - * 'erMyClass2' => 'Me/myclass2.php', - * ); - * ?> - * - * - * "your_autoload.php": - * - * 'You/yourclass1.php', - * 'erYourClass2' => 'You/yourclass2.php', - * ); - * ?> - * - * - * The directory structure for the external repository is then: - * - * ./repos/autoloads/my_autoload.php - * ./repos/autoloads/you_autoload.php - * ./repos/Me/myclass1.php - * ./repos/Me/myclass2.php - * ./repos/You/yourclass1.php - * ./repos/You/yourclass2.php - * - * - * To use this repository with the autoload mechanism you have to use the - * following code: - * - * - * - * - * @throws ezcBaseFileNotFoundException if $autoloadDirPath or $basePath do not exist. - * @param string $basePath - * @param string $autoloadDirPath - * @param string $prefix - */ - public static function addClassRepository( $basePath, $autoloadDirPath = null, $prefix = null ) - { - // check if base path exists - if ( !is_dir( $basePath ) ) - { - throw new ezcBaseFileNotFoundException( $basePath, 'base directory' ); - } - - // calculate autoload path if it wasn't given - if ( is_null( $autoloadDirPath ) ) - { - $autoloadDirPath = $basePath . '/autoload'; - } - - // check if autoload dir exists - if ( !is_dir( $autoloadDirPath ) ) - { - throw new ezcBaseFileNotFoundException( $autoloadDirPath, 'autoload directory' ); - } - - // add info to $repositoryDirs - if ( $prefix === null ) - { - $array = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath ); - - // add info to the list of extra dirs - ezcBase::$repositoryDirs[] = $array; - } - else - { - if ( array_key_exists( $prefix, ezcBase::$repositoryDirs ) ) - { - throw new ezcBaseDoubleClassRepositoryPrefixException( $prefix, $basePath, $autoloadDirPath ); - } - - // add info to the list of extra dirs, and use the prefix to identify the new repository. - ezcBase::$repositoryDirs[$prefix] = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath ); - } - } - - /** - * Returns the base path of the eZ Components installation - * - * This method returns the base path, including a trailing directory - * separator. - * - * @return string - */ - public static function getInstallationPath() - { - self::setPackageDir(); - - $path = realpath( self::$packageDir ); - if ( substr( $path, -1 ) !== DIRECTORY_SEPARATOR ) - { - $path .= DIRECTORY_SEPARATOR; - } - return $path; - } - - /** - * Sets the development mode to the one specified. - * - * @param int $runMode - */ - public static function setRunMode( $runMode ) - { - if ( !in_array( $runMode, array( ezcBase::MODE_PRODUCTION, ezcBase::MODE_DEVELOPMENT ) ) ) - { - throw new ezcBaseValueException( 'runMode', $runMode, 'ezcBase::MODE_PRODUCTION or ezcBase::MODE_DEVELOPMENT' ); - } - - self::$runMode = $runMode; - } - - /** - * Returns the current development mode. - * - * @return int - */ - public static function getRunMode() - { - return self::$runMode; - } - - /** - * Returns true when we are in development mode. - * - * @return bool - */ - public static function inDevMode() - { - return self::$runMode == ezcBase::MODE_DEVELOPMENT; - } - - /** - * Returns the installation method - * - * Possible return values are 'custom', 'devel', 'tarball' and 'pear'. Only - * 'tarball' and 'pear' are returned for user-installed versions. - * - * @return string - */ - public static function getInstallMethod() - { - return self::$libraryMode; - } -} -?> +array) + */ + protected static $repositoryDirs = array(); + + /** + * This variable stores all the elements from the autoload arrays. When a + * new autoload file is loaded, their files are added to this array. + * + * @var array(string=>string) + */ + protected static $autoloadArray = array(); + + /** + * This variable stores all the elements from the autoload arrays for + * external repositories. When a new autoload file is loaded, their files + * are added to this array. + * + * @var array(string=>string) + */ + protected static $externalAutoloadArray = array(); + + /** + * Options for the ezcBase class. + * + * @var ezcBaseOptions + */ + static private $options; + + /** + * Associates an option object with this static class. + * + * @param ezcBaseAutoloadOptions $options + */ + static public function setOptions( ezcBaseAutoloadOptions $options ) + { + self::$options = $options; + } + + /** + * Tries to autoload the given className. If the className could be found + * this method returns true, otherwise false. + * + * This class caches the requested class names (including the ones who + * failed to load). + * + * @param string $className The name of the class that should be loaded. + * + * @return bool + */ + public static function autoload( $className ) + { + ezcBase::setPackageDir(); + + // Check whether the classname is already in the cached autoloadArray. + if ( array_key_exists( $className, ezcBase::$autoloadArray ) ) + { + // Is it registered as 'unloadable'? + if ( ezcBase::$autoloadArray[$className] == false ) + { + return false; + } + ezcBase::loadFile( ezcBase::$autoloadArray[$className] ); + + return true; + } + + // Check whether the classname is already in the cached autoloadArray + // for external repositories. + if ( array_key_exists( $className, ezcBase::$externalAutoloadArray ) ) + { + // Is it registered as 'unloadable'? + if ( ezcBase::$externalAutoloadArray[$className] == false ) + { + return false; + } + ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] ); + + return true; + } + + // Not cached, so load the autoload from the package. + // Matches the first and optionally the second 'word' from the classname. + $fileNames = array(); + if ( preg_match( "/^([a-z0-9]*)([A-Z][a-z0-9]*)([A-Z][a-z0-9]*)?/", $className, $matches ) !== false ) + { + $autoloadFile = ""; + // Try to match with both names, if available. + switch ( sizeof( $matches ) ) + { + case 4: + // check for x_y_autoload.php + $autoloadFile = strtolower( "{$matches[2]}_{$matches[3]}_autoload.php" ); + $fileNames[] = $autoloadFile; + if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) + { + return true; + } + // break intentionally missing. + + case 3: + // check for x_autoload.php + $autoloadFile = strtolower( "{$matches[2]}_autoload.php" ); + $fileNames[] = $autoloadFile; + if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) + { + return true; + } + + // check for autoload.php + $autoloadFile = 'autoload.php'; + $fileNames[] = $autoloadFile; + if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) ) + { + return true; + } + break; + } + + // Maybe there is another autoload available. + // Register this classname as false. + ezcBase::$autoloadArray[$className] = false; + } + + $path = ezcBase::$packageDir . 'autoload/'; + $realPath = realpath( $path ); + + if ( $realPath == '' ) + { + // Can not be tested, because if this happens, then the autoload + // environment has not been set-up correctly. + trigger_error( "Couldn't find autoload directory '$path'", E_USER_ERROR ); + } + + $dirs = self::getRepositoryDirectories(); + if ( ezcBase::$options && ezcBase::$options->debug ) + { + throw new ezcBaseAutoloadException( $className, $fileNames, $dirs ); + } + + return false; + } + + /** + * Sets the current working directory to $directory. + * + * @param string $directory + */ + public static function setWorkingDirectory( $directory ) + { + self::$libraryMode = 'custom'; + self::$currentWorkingDirectory = $directory; + } + + /** + * Figures out the base path of the eZ Components installation. + * + * It stores the path that it finds in a static member variable. The path + * depends on the installation method of the eZ Components. The SVN version + * has a different path than the PEAR installed version. + */ + protected static function setPackageDir() + { + if ( ezcBase::$packageDir !== null ) + { + return; + } + + // Get the path to the components. + $baseDir = dirname( __FILE__ ); + + switch ( ezcBase::$libraryMode ) + { + case "custom": + ezcBase::$packageDir = self::$currentWorkingDirectory . '/'; + break; + case "devel": + case "tarball": + ezcBase::$packageDir = $baseDir. "/../../"; + break; + case "pear"; + ezcBase::$packageDir = $baseDir. "/../"; + break; + } + } + + /** + * Tries to load the autoload array and, if loaded correctly, includes the class. + * + * @param string $fileName Name of the autoload file. + * @param string $className Name of the class that should be autoloaded. + * @param string $prefix The prefix of the class repository. + * + * @return bool True is returned when the file is correctly loaded. + * Otherwise false is returned. + */ + protected static function requireFile( $fileName, $className, $prefix ) + { + $autoloadDir = ezcBase::$packageDir . "autoload/"; + + // We need the full path to the fileName. The method file_exists() doesn't + // automatically check the (php.ini) library paths. Therefore: + // file_exists( "ezc/autoload/$fileName" ) doesn't work. + if ( $prefix === 'ezc' && file_exists( "$autoloadDir$fileName" ) ) + { + $array = require( "$autoloadDir$fileName" ); + + if ( is_array( $array) && array_key_exists( $className, $array ) ) + { + // Add the array to the cache, and include the requested file. + ezcBase::$autoloadArray = array_merge( ezcBase::$autoloadArray, $array ); + if ( ezcBase::$options !== null && ezcBase::$options->preload && !preg_match( '/Exception$/', $className ) ) + { + foreach ( $array as $loadClassName => $file ) + { + if ( $loadClassName !== 'ezcBase' && !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false ) && !preg_match( '/Exception$/', $loadClassName ) /*&& !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false )*/ ) + { + ezcBase::loadFile( ezcBase::$autoloadArray[$loadClassName] ); + } + } + } + else + { + ezcBase::loadFile( ezcBase::$autoloadArray[$className] ); + } + return true; + } + } + + // It is not in components autoload/ dir. + // try to search in additional dirs. + foreach ( ezcBase::$repositoryDirs as $repositoryPrefix => $extraDir ) + { + if ( gettype( $repositoryPrefix ) === 'string' && $repositoryPrefix !== $prefix ) + { + continue; + } + + if ( file_exists( $extraDir['autoloadDirPath'] . '/' . $fileName ) ) + { + $array = array(); + $originalArray = require( $extraDir['autoloadDirPath'] . '/' . $fileName ); + + // Building paths. + // Resulting path to class definition file consists of: + // path to extra directory with autoload file + + // basePath provided for current extra directory + + // path to class definition file stored in autoload file. + foreach ( $originalArray as $class => $classPath ) + { + $array[$class] = $extraDir['basePath'] . '/' . $classPath; + } + + if ( is_array( $array ) && array_key_exists( $className, $array ) ) + { + // Add the array to the cache, and include the requested file. + ezcBase::$externalAutoloadArray = array_merge( ezcBase::$externalAutoloadArray, $array ); + ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] ); + return true; + } + } + } + + // Nothing found :-(. + return false; + } + + /** + * Loads, require(), the given file name. If we are in development mode, + * "/src/" is inserted into the path. + * + * @param string $file The name of the file that should be loaded. + */ + protected static function loadFile( $file ) + { + switch ( ezcBase::$libraryMode ) + { + case "devel": + case "tarball": + list( $first, $second ) = explode( '/', $file, 2 ); + $file = $first . "/src/" . $second; + break; + + case "custom": + list( $first, $second ) = explode( '/', $file, 2 ); + // Add the "src/" after the package name. + if ( $first == 'Base' || $first == 'UnitTest' ) + { + list( $first, $second ) = explode( '/', $file, 2 ); + $file = $first . "/src/" . $second; + } + else + { + list( $first, $second, $third ) = explode( '/', $file, 3 ); + $file = $first . '/' . $second . "/src/" . $third; + } + break; + + case "pear": + /* do nothing, it's already correct */ + break; + } + + if ( file_exists( ezcBase::$packageDir . $file ) ) + { + require( ezcBase::$packageDir . $file ); + } + else + { + // Can not be tested, because if this happens, then one of the + // components has a broken autoload file. + throw new ezcBaseFileNotFoundException( ezcBase::$packageDir.$file ); + } + } + + /** + * Loads, require(), the given file name from an external package. + * + * @param string $file The name of the file that should be loaded. + */ + protected static function loadExternalFile( $file ) + { + if ( file_exists( $file ) ) + { + require( $file ); + } + else + { + throw new ezcBaseFileNotFoundException( $file ); + } + } + + /** + * Checks for dependencies on PHP versions or extensions + * + * The function as called by the $component component checks for the $type + * dependency. The dependency $type is compared against the $value. The + * function aborts the script if the dependency is not matched. + * + * @param string $component + * @param int $type + * @param mixed $value + */ + public static function checkDependency( $component, $type, $value ) + { + switch ( $type ) + { + case self::DEP_PHP_EXTENSION: + if ( extension_loaded( $value ) ) + { + return; + } + else + { + // Can not be tested as it would abort the PHP script. + die( "\nThe {$component} component depends on the default PHP extension '{$value}', which is not loaded.\n" ); + } + break; + + case self::DEP_PHP_VERSION: + $phpVersion = phpversion(); + if ( version_compare( $phpVersion, $value, '>=' ) ) + { + return; + } + else + { + // Can not be tested as it would abort the PHP script. + die( "\nThe {$component} component depends on the PHP version '{$value}', but the current version is '{$phpVersion}'.\n" ); + } + break; + } + } + + /** + * Return the list of directories that contain class repositories. + * + * The path to the eZ components directory is always included in the result + * array. Each element in the returned array has the format of: + * packageDirectory => ezcBaseRepositoryDirectory + * + * @return array(string=>ezcBaseRepositoryDirectory) + */ + public static function getRepositoryDirectories() + { + $autoloadDirs = array(); + ezcBase::setPackageDir(); + $repositoryDir = self::$currentWorkingDirectory ? self::$currentWorkingDirectory : ( realpath( dirname( __FILE__ ) . '/../../' ) ); + $autoloadDirs['ezc'] = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_INTERNAL, $repositoryDir, $repositoryDir . "/autoload" ); + + foreach ( ezcBase::$repositoryDirs as $extraDirKey => $extraDirArray ) + { + $repositoryDirectory = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_EXTERNAL, realpath( $extraDirArray['basePath'] ), realpath( $extraDirArray['autoloadDirPath'] ) ); + $autoloadDirs[$extraDirKey] = $repositoryDirectory; + } + + return $autoloadDirs; + } + + /** + * Adds an additional class repository. + * + * Used for adding class repositoryies outside the eZ components to be + * loaded by the autoload system. + * + * This function takes two arguments: $basePath is the base path for the + * whole class repository and $autoloadDirPath the path where autoload + * files for this repository are found. The paths in the autoload files are + * relative to the package directory as specified by the $basePath + * argument. I.e. class definition file will be searched at location + * $basePath + path to the class definition file as stored in the autoload + * file. + * + * addClassRepository() should be called somewhere in code before external classes + * are used. + * + * Example: + * Take the following facts: + * + * + * In this case you would need to create the following files in + * "./repos/autoloads". Please note that the part before _autoload.php in + * the filename is the first part of the classname, not considering + * the all lower-case letter prefix. + * + * "my_autoload.php": + * + * 'Me/myclass1.php', + * 'erMyClass2' => 'Me/myclass2.php', + * ); + * ?> + * + * + * "your_autoload.php": + * + * 'You/yourclass1.php', + * 'erYourClass2' => 'You/yourclass2.php', + * ); + * ?> + * + * + * The directory structure for the external repository is then: + * + * ./repos/autoloads/my_autoload.php + * ./repos/autoloads/you_autoload.php + * ./repos/Me/myclass1.php + * ./repos/Me/myclass2.php + * ./repos/You/yourclass1.php + * ./repos/You/yourclass2.php + * + * + * To use this repository with the autoload mechanism you have to use the + * following code: + * + * + * + * + * @throws ezcBaseFileNotFoundException if $autoloadDirPath or $basePath do not exist. + * @param string $basePath + * @param string $autoloadDirPath + * @param string $prefix + */ + public static function addClassRepository( $basePath, $autoloadDirPath = null, $prefix = null ) + { + // check if base path exists + if ( !is_dir( $basePath ) ) + { + throw new ezcBaseFileNotFoundException( $basePath, 'base directory' ); + } + + // calculate autoload path if it wasn't given + if ( is_null( $autoloadDirPath ) ) + { + $autoloadDirPath = $basePath . '/autoload'; + } + + // check if autoload dir exists + if ( !is_dir( $autoloadDirPath ) ) + { + throw new ezcBaseFileNotFoundException( $autoloadDirPath, 'autoload directory' ); + } + + // add info to $repositoryDirs + if ( $prefix === null ) + { + $array = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath ); + + // add info to the list of extra dirs + ezcBase::$repositoryDirs[] = $array; + } + else + { + if ( array_key_exists( $prefix, ezcBase::$repositoryDirs ) ) + { + throw new ezcBaseDoubleClassRepositoryPrefixException( $prefix, $basePath, $autoloadDirPath ); + } + + // add info to the list of extra dirs, and use the prefix to identify the new repository. + ezcBase::$repositoryDirs[$prefix] = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath ); + } + } + + /** + * Returns the base path of the eZ Components installation + * + * This method returns the base path, including a trailing directory + * separator. + * + * @return string + */ + public static function getInstallationPath() + { + self::setPackageDir(); + + $path = realpath( self::$packageDir ); + if ( substr( $path, -1 ) !== DIRECTORY_SEPARATOR ) + { + $path .= DIRECTORY_SEPARATOR; + } + return $path; + } + + /** + * Sets the development mode to the one specified. + * + * @param int $runMode + */ + public static function setRunMode( $runMode ) + { + if ( !in_array( $runMode, array( ezcBase::MODE_PRODUCTION, ezcBase::MODE_DEVELOPMENT ) ) ) + { + throw new ezcBaseValueException( 'runMode', $runMode, 'ezcBase::MODE_PRODUCTION or ezcBase::MODE_DEVELOPMENT' ); + } + + self::$runMode = $runMode; + } + + /** + * Returns the current development mode. + * + * @return int + */ + public static function getRunMode() + { + return self::$runMode; + } + + /** + * Returns true when we are in development mode. + * + * @return bool + */ + public static function inDevMode() + { + return self::$runMode == ezcBase::MODE_DEVELOPMENT; + } + + /** + * Returns the installation method + * + * Possible return values are 'custom', 'devel', 'tarball' and 'pear'. Only + * 'tarball' and 'pear' are returned for user-installed versions. + * + * @return string + */ + public static function getInstallMethod() + { + return self::$libraryMode; + } +} +?> diff --git a/library/ezc/Base/src/exceptions/autoload.php b/library/ezc/Base/src/exceptions/autoload.php index 815d7089ca..fba16491ce 100644 --- a/library/ezc/Base/src/exceptions/autoload.php +++ b/library/ezc/Base/src/exceptions/autoload.php @@ -1,38 +1,38 @@ -autoloadPath ); - } - parent::__construct( "Could not find a class to file mapping for '{$className}'. Searched for ". implode( ', ', $files ) . " in: " . implode( ', ', $paths ) ); - } -} -?> +autoloadPath ); + } + parent::__construct( "Could not find a class to file mapping for '{$className}'. Searched for ". implode( ', ', $files ) . " in: " . implode( ', ', $paths ) ); + } +} +?> diff --git a/library/ezc/Base/src/exceptions/double_class_repository_prefix.php b/library/ezc/Base/src/exceptions/double_class_repository_prefix.php index de34b9227a..1c33df1091 100644 --- a/library/ezc/Base/src/exceptions/double_class_repository_prefix.php +++ b/library/ezc/Base/src/exceptions/double_class_repository_prefix.php @@ -1,34 +1,34 @@ - + diff --git a/library/ezc/Base/src/exceptions/exception.php b/library/ezc/Base/src/exceptions/exception.php index dc99ba6fc3..c36164ffc5 100644 --- a/library/ezc/Base/src/exceptions/exception.php +++ b/library/ezc/Base/src/exceptions/exception.php @@ -1,43 +1,43 @@ -originalMessage = $message; - - if ( php_sapi_name() == 'cli' ) - { - parent::__construct( $message ); - } - else - { - parent::__construct( htmlspecialchars( $message ) ); - } - } -} -?> +originalMessage = $message; + + if ( php_sapi_name() == 'cli' ) + { + parent::__construct( $message ); + } + else + { + parent::__construct( htmlspecialchars( $message ) ); + } + } +} +?> diff --git a/library/ezc/Base/src/exceptions/extension_not_found.php b/library/ezc/Base/src/exceptions/extension_not_found.php index a53e5937d5..4e6ff88e7c 100644 --- a/library/ezc/Base/src/exceptions/extension_not_found.php +++ b/library/ezc/Base/src/exceptions/extension_not_found.php @@ -1,38 +1,38 @@ - + diff --git a/library/ezc/Base/src/exceptions/file_exception.php b/library/ezc/Base/src/exceptions/file_exception.php index ba78af6069..945b3d5dfa 100644 --- a/library/ezc/Base/src/exceptions/file_exception.php +++ b/library/ezc/Base/src/exceptions/file_exception.php @@ -1,25 +1,25 @@ - + diff --git a/library/ezc/Base/src/exceptions/file_io.php b/library/ezc/Base/src/exceptions/file_io.php index 683bf0854d..3d635a5bc1 100644 --- a/library/ezc/Base/src/exceptions/file_io.php +++ b/library/ezc/Base/src/exceptions/file_io.php @@ -1,50 +1,50 @@ - + diff --git a/library/ezc/Base/src/exceptions/file_not_found.php b/library/ezc/Base/src/exceptions/file_not_found.php index 737a38c02f..9b65afccb3 100644 --- a/library/ezc/Base/src/exceptions/file_not_found.php +++ b/library/ezc/Base/src/exceptions/file_not_found.php @@ -1,43 +1,43 @@ - + diff --git a/library/ezc/Base/src/exceptions/file_permission.php b/library/ezc/Base/src/exceptions/file_permission.php index f101c0f45b..1bbd243ad6 100644 --- a/library/ezc/Base/src/exceptions/file_permission.php +++ b/library/ezc/Base/src/exceptions/file_permission.php @@ -1,63 +1,63 @@ - + diff --git a/library/ezc/Base/src/exceptions/functionality_not_supported.php b/library/ezc/Base/src/exceptions/functionality_not_supported.php index 40eb245bc8..3fa617ce60 100644 --- a/library/ezc/Base/src/exceptions/functionality_not_supported.php +++ b/library/ezc/Base/src/exceptions/functionality_not_supported.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Base/src/exceptions/init_callback_configured.php b/library/ezc/Base/src/exceptions/init_callback_configured.php index a2a28e0090..95f158f4d6 100644 --- a/library/ezc/Base/src/exceptions/init_callback_configured.php +++ b/library/ezc/Base/src/exceptions/init_callback_configured.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Base/src/exceptions/invalid_callback_class.php b/library/ezc/Base/src/exceptions/invalid_callback_class.php index b07e55ef09..80c709526c 100644 --- a/library/ezc/Base/src/exceptions/invalid_callback_class.php +++ b/library/ezc/Base/src/exceptions/invalid_callback_class.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Base/src/exceptions/invalid_parent_class.php b/library/ezc/Base/src/exceptions/invalid_parent_class.php index 0bce0ca6f0..3ab53461b6 100644 --- a/library/ezc/Base/src/exceptions/invalid_parent_class.php +++ b/library/ezc/Base/src/exceptions/invalid_parent_class.php @@ -1,29 +1,29 @@ - + diff --git a/library/ezc/Base/src/exceptions/property_not_found.php b/library/ezc/Base/src/exceptions/property_not_found.php index acc44f02dc..d4d549f6a3 100644 --- a/library/ezc/Base/src/exceptions/property_not_found.php +++ b/library/ezc/Base/src/exceptions/property_not_found.php @@ -1,30 +1,30 @@ - + diff --git a/library/ezc/Base/src/exceptions/property_permission.php b/library/ezc/Base/src/exceptions/property_permission.php index b9023591cc..1ee4961795 100644 --- a/library/ezc/Base/src/exceptions/property_permission.php +++ b/library/ezc/Base/src/exceptions/property_permission.php @@ -1,42 +1,42 @@ - + diff --git a/library/ezc/Base/src/exceptions/setting_not_found.php b/library/ezc/Base/src/exceptions/setting_not_found.php index efe455df63..df1ebcfb80 100644 --- a/library/ezc/Base/src/exceptions/setting_not_found.php +++ b/library/ezc/Base/src/exceptions/setting_not_found.php @@ -1,29 +1,29 @@ - + diff --git a/library/ezc/Base/src/exceptions/setting_value.php b/library/ezc/Base/src/exceptions/setting_value.php index 10c9e18b46..d436f379c0 100644 --- a/library/ezc/Base/src/exceptions/setting_value.php +++ b/library/ezc/Base/src/exceptions/setting_value.php @@ -1,42 +1,42 @@ - + diff --git a/library/ezc/Base/src/exceptions/value.php b/library/ezc/Base/src/exceptions/value.php index 33182c2235..fe8498a44b 100644 --- a/library/ezc/Base/src/exceptions/value.php +++ b/library/ezc/Base/src/exceptions/value.php @@ -1,43 +1,43 @@ - + diff --git a/library/ezc/Base/src/exceptions/whatever.php b/library/ezc/Base/src/exceptions/whatever.php index aac101ed55..75568b5cb1 100644 --- a/library/ezc/Base/src/exceptions/whatever.php +++ b/library/ezc/Base/src/exceptions/whatever.php @@ -1,40 +1,40 @@ - + diff --git a/library/ezc/Base/src/ezc_bootstrap.php b/library/ezc/Base/src/ezc_bootstrap.php index d02007dc16..5d8b1b082c 100644 --- a/library/ezc/Base/src/ezc_bootstrap.php +++ b/library/ezc/Base/src/ezc_bootstrap.php @@ -1,40 +1,40 @@ - + diff --git a/library/ezc/Base/src/features.php b/library/ezc/Base/src/features.php index b26fdd2469..10475651d9 100644 --- a/library/ezc/Base/src/features.php +++ b/library/ezc/Base/src/features.php @@ -1,351 +1,365 @@ - - * - * - * - * @package Base - * @version //autogentag// - */ -class ezcBaseFeatures -{ - /** - * Used to store the path of the ImageMagick convert utility. - * - * It is initialized in the {@link getImageConvertExecutable()} function. - * - * @var string - */ - private static $imageConvert = null; - - /** - * Used to store the path of the ImageMagick identify utility. - * - * It is initialized in the {@link getImageIdentifyExecutable()} function. - * - * @var string - */ - private static $imageIdentify = null; - - /** - * Used to store the operating system. - * - * It is initialized in the {@link os()} function. - * - * @var string - */ - private static $os = null; - - /** - * Determines if hardlinks are supported. - * - * @return bool - */ - public static function supportsLink() - { - return function_exists( 'link' ); - } - - /** - * Determines if symlinks are supported. - * - * @return bool - */ - public static function supportsSymLink() - { - return function_exists( 'symlink' ); - } - - /** - * Determines if posix uids are supported. - * - * @return bool - */ - public static function supportsUserId() - { - return function_exists( 'posix_getpwuid' ); - } - - /** - * Determines if the ImageMagick convert utility is installed. - * - * @return bool - */ - public static function hasImageConvert() - { - return !is_null( self::getImageConvertExecutable() ); - } - - /** - * Returns the path to the ImageMagick convert utility. - * - * On Linux, Unix,... it will return something like: /usr/bin/convert - * On Windows it will return something like: C:\Windows\System32\convert.exe - * - * @return string - */ - public static function getImageConvertExecutable() - { - if ( !is_null( self::$imageConvert ) ) - { - return self::$imageConvert; - } - return ( self::$imageConvert = self::findExecutableInPath( 'convert' ) ); - } - - /** - * Determines if the ImageMagick identify utility is installed. - * - * @return bool - */ - public static function hasImageIdentify() - { - return !is_null( self::getImageIdentifyExecutable() ); - } - - /** - * Returns the path to the ImageMagick identify utility. - * - * On Linux, Unix,... it will return something like: /usr/bin/identify - * On Windows it will return something like: C:\Windows\System32\identify.exe - * - * @return string - */ - public static function getImageIdentifyExecutable() - { - if ( !is_null( self::$imageIdentify ) ) - { - return self::$imageIdentify; - } - return ( self::$imageIdentify = self::findExecutableInPath( 'identify' ) ); - } - - /** - * Determines if the specified extension is loaded. - * - * If $version is specified, the specified extension will be tested also - * against the version of the loaded extension. - * - * Examples: - * - * hasExtensionSupport( 'gzip' ); - * - * will return true if gzip extension is loaded. - * - * - * hasExtensionSupport( 'pdo_mysql', '1.0.2' ); - * - * will return true if pdo_mysql extension is loaded and its version is at least 1.0.2. - * - * @param string $extension - * @param string $version - * @return bool - */ - public static function hasExtensionSupport( $extension, $version = null ) - { - if ( is_null( $version ) ) - { - return extension_loaded( $extension ); - } - return extension_loaded( $extension ) && version_compare( phpversion( $extension ), $version, ">=" ) ; - } - - /** - * Determines if the specified function is available. - * - * Examples: - * - * ezcBaseFeatures::hasFunction( 'imagepstext' ); - * - * will return true if support for Type 1 fonts is available with your GD - * extension. - * - * @param string $functionName - * @return bool - */ - public static function hasFunction( $functionName ) - { - return function_exists( $functionName ); - } - - /** - * Returns if a given class exists. - * Checks for a given class name and returns if this class exists or not. - * Catches the ezcBaseAutoloadException and returns false, if it was thrown. - * - * @param string $className The class to check for. - * @param bool $autoload True to use __autoload(), otherwise false. - * @return bool True if the class exists. Otherwise false. - */ - public static function classExists( $className, $autoload = true ) - { - try - { - if ( class_exists( $className, $autoload ) ) - { - return true; - } - return false; - } - catch ( ezcBaseAutoloadException $e ) - { - return false; - } - } - - /** - * Returns the operating system on which PHP is running. - * - * This method returns a sanitized form of the OS name, example - * return values are "Windows", "Mac", "Linux" and "FreeBSD". In - * all other cases it returns the value of the internal PHP constant - * PHP_OS. - * - * @return string - */ - public static function os() - { - if ( is_null( self::$os ) ) - { - $uname = php_uname( 's' ); - if ( substr( $uname, 0, 7 ) == 'Windows' ) - { - self::$os = 'Windows'; - } - elseif ( substr( $uname, 0, 3 ) == 'Mac' ) - { - self::$os = 'Mac'; - } - elseif ( strtolower( $uname ) == 'linux' ) - { - self::$os = 'Linux'; - } - elseif ( strtolower( substr( $uname, 0, 7 ) ) == 'freebsd' ) - { - self::$os = 'FreeBSD'; - } - else - { - self::$os = PHP_OS; - } - } - return self::$os; - } - - /** - * Returns the path of the specified executable, if it can be found in the system's path. - * - * It scans the PATH enviroment variable based on the OS to find the - * $fileName. For Windows, the path is with \, not /. If $fileName is not - * found, it returns null. - * - * @todo consider using getenv( 'PATH' ) instead of $_ENV['PATH'] - * (but that won't work under IIS) - * - * @param string $fileName - * @return string - */ - public static function findExecutableInPath( $fileName ) - { - if ( array_key_exists( 'PATH', $_ENV ) ) - { - $envPath = trim( $_ENV['PATH'] ); - } - else if ( ( $envPath = getenv( 'PATH' ) ) !== false ) - { - $envPath = trim( $envPath ); - } - if ( is_string( $envPath ) && strlen( trim( $envPath ) ) == 0 ) - { - $envPath = false; - } - - switch ( self::os() ) - { - case 'Unix': - case 'FreeBSD': - case 'Mac': - case 'MacOS': - case 'Darwin': - case 'Linux': - case 'SunOS': - if ( $envPath ) - { - $dirs = explode( ':', $envPath ); - foreach ( $dirs as $dir ) - { - // The @-operator is used here mainly to avoid - // open_basedir warnings. If open_basedir (or any other - // circumstance) prevents the desired file from being - // accessed, it is fine for file_exists() to return - // false, since it is useless for use then, anyway. - if ( file_exists( "{$dir}/{$fileName}" ) ) - { - return "{$dir}/{$fileName}"; - } - } - } - // The @-operator is used here mainly to avoid open_basedir - // warnings. If open_basedir (or any other circumstance) - // prevents the desired file from being accessed, it is fine - // for file_exists() to return false, since it is useless for - // use then, anyway. - elseif ( @file_exists( "./{$fileName}" ) ) - { - return $fileName; - } - break; - case 'Windows': - if ( $envPath ) - { - $dirs = explode( ';', $envPath ); - foreach ( $dirs as $dir ) - { - // The @-operator is used here mainly to avoid - // open_basedir warnings. If open_basedir (or any other - // circumstance) prevents the desired file from being - // accessed, it is fine for file_exists() to return - // false, since it is useless for use then, anyway. - if ( @file_exists( "{$dir}\\{$fileName}.exe" ) ) - { - return "{$dir}\\{$fileName}.exe"; - } - } - } - // The @-operator is used here mainly to avoid open_basedir - // warnings. If open_basedir (or any other circumstance) - // prevents the desired file from being accessed, it is fine - // for file_exists() to return false, since it is useless for - // use then, anyway. - elseif ( @file_exists( "{$fileName}.exe" ) ) - { - return "{$fileName}.exe"; - } - break; - } - return null; - } -} -?> + + * + * + * + * @package Base + * @version 1.7 + */ +class ezcBaseFeatures +{ + /** + * Used to store the path of the ImageMagick convert utility. + * + * It is initialized in the {@link getImageConvertExecutable()} function. + * + * @var string + */ + private static $imageConvert = null; + + /** + * Used to store the path of the ImageMagick identify utility. + * + * It is initialized in the {@link getImageIdentifyExecutable()} function. + * + * @var string + */ + private static $imageIdentify = null; + + /** + * Used to store the operating system. + * + * It is initialized in the {@link os()} function. + * + * @var string + */ + private static $os = null; + + /** + * Determines if hardlinks are supported. + * + * @return bool + */ + public static function supportsLink() + { + return function_exists( 'link' ); + } + + /** + * Determines if symlinks are supported. + * + * @return bool + */ + public static function supportsSymLink() + { + return function_exists( 'symlink' ); + } + + /** + * Determines if posix uids are supported. + * + * @return bool + */ + public static function supportsUserId() + { + return function_exists( 'posix_getpwuid' ); + } + + /** + * Determines if the ImageMagick convert utility is installed. + * + * @return bool + */ + public static function hasImageConvert() + { + return !is_null( self::getImageConvertExecutable() ); + } + + /** + * Returns the path to the ImageMagick convert utility. + * + * On Linux, Unix,... it will return something like: /usr/bin/convert + * On Windows it will return something like: C:\Windows\System32\convert.exe + * + * @return string + */ + public static function getImageConvertExecutable() + { + if ( !is_null( self::$imageConvert ) ) + { + return self::$imageConvert; + } + return ( self::$imageConvert = self::findExecutableInPath( 'convert' ) ); + } + + /** + * Determines if the ImageMagick identify utility is installed. + * + * @return bool + */ + public static function hasImageIdentify() + { + return !is_null( self::getImageIdentifyExecutable() ); + } + + /** + * Returns the path to the ImageMagick identify utility. + * + * On Linux, Unix,... it will return something like: /usr/bin/identify + * On Windows it will return something like: C:\Windows\System32\identify.exe + * + * @return string + */ + public static function getImageIdentifyExecutable() + { + if ( !is_null( self::$imageIdentify ) ) + { + return self::$imageIdentify; + } + return ( self::$imageIdentify = self::findExecutableInPath( 'identify' ) ); + } + + /** + * Determines if the specified extension is loaded. + * + * If $version is specified, the specified extension will be tested also + * against the version of the loaded extension. + * + * Examples: + * + * hasExtensionSupport( 'gzip' ); + * + * will return true if gzip extension is loaded. + * + * + * hasExtensionSupport( 'pdo_mysql', '1.0.2' ); + * + * will return true if pdo_mysql extension is loaded and its version is at least 1.0.2. + * + * @param string $extension + * @param string $version + * @return bool + */ + public static function hasExtensionSupport( $extension, $version = null ) + { + if ( is_null( $version ) ) + { + return extension_loaded( $extension ); + } + return extension_loaded( $extension ) && version_compare( phpversion( $extension ), $version, ">=" ) ; + } + + /** + * Determines if the specified function is available. + * + * Examples: + * + * ezcBaseFeatures::hasFunction( 'imagepstext' ); + * + * will return true if support for Type 1 fonts is available with your GD + * extension. + * + * @param string $functionName + * @return bool + */ + public static function hasFunction( $functionName ) + { + return function_exists( $functionName ); + } + + /** + * Returns if a given class exists. + * Checks for a given class name and returns if this class exists or not. + * Catches the ezcBaseAutoloadException and returns false, if it was thrown. + * + * @param string $className The class to check for. + * @param bool $autoload True to use __autoload(), otherwise false. + * @return bool True if the class exists. Otherwise false. + */ + public static function classExists( $className, $autoload = true ) + { + try + { + if ( class_exists( $className, $autoload ) ) + { + return true; + } + return false; + } + catch ( ezcBaseAutoloadException $e ) + { + return false; + } + } + + /** + * Returns the operating system on which PHP is running. + * + * This method returns a sanitized form of the OS name, example + * return values are "Windows", "Mac", "Linux" and "FreeBSD". In + * all other cases it returns the value of the internal PHP constant + * PHP_OS. + * + * @return string + */ + public static function os() + { + if ( is_null( self::$os ) ) + { + $uname = php_uname( 's' ); + if ( substr( $uname, 0, 7 ) == 'Windows' ) + { + self::$os = 'Windows'; + } + elseif ( substr( $uname, 0, 3 ) == 'Mac' ) + { + self::$os = 'Mac'; + } + elseif ( strtolower( $uname ) == 'linux' ) + { + self::$os = 'Linux'; + } + elseif ( strtolower( substr( $uname, 0, 7 ) ) == 'freebsd' ) + { + self::$os = 'FreeBSD'; + } + else + { + self::$os = PHP_OS; + } + } + return self::$os; + } + + /** + * Returns the path of the specified executable, if it can be found in the system's path. + * + * It scans the PATH enviroment variable based on the OS to find the + * $fileName. For Windows, the path is with \, not /. If $fileName is not + * found, it returns null. + * + * @todo consider using getenv( 'PATH' ) instead of $_ENV['PATH'] + * (but that won't work under IIS) + * + * @param string $fileName + * @return string + */ + public static function findExecutableInPath( $fileName ) + { + if ( array_key_exists( 'PATH', $_ENV ) ) + { + $envPath = trim( $_ENV['PATH'] ); + } + else if ( ( $envPath = getenv( 'PATH' ) ) !== false ) + { + $envPath = trim( $envPath ); + } + if ( is_string( $envPath ) && strlen( trim( $envPath ) ) == 0 ) + { + $envPath = false; + } + + switch ( self::os() ) + { + case 'Unix': + case 'FreeBSD': + case 'Mac': + case 'MacOS': + case 'Darwin': + case 'Linux': + case 'SunOS': + if ( $envPath ) + { + $dirs = explode( ':', $envPath ); + foreach ( $dirs as $dir ) + { + // The @-operator is used here mainly to avoid + // open_basedir warnings. If open_basedir (or any other + // circumstance) prevents the desired file from being + // accessed, it is fine for file_exists() to return + // false, since it is useless for use then, anyway. + if ( file_exists( "{$dir}/{$fileName}" ) ) + { + return "{$dir}/{$fileName}"; + } + } + } + // The @-operator is used here mainly to avoid open_basedir + // warnings. If open_basedir (or any other circumstance) + // prevents the desired file from being accessed, it is fine + // for file_exists() to return false, since it is useless for + // use then, anyway. + elseif ( @file_exists( "./{$fileName}" ) ) + { + return $fileName; + } + break; + case 'Windows': + if ( $envPath ) + { + $dirs = explode( ';', $envPath ); + foreach ( $dirs as $dir ) + { + // The @-operator is used here mainly to avoid + // open_basedir warnings. If open_basedir (or any other + // circumstance) prevents the desired file from being + // accessed, it is fine for file_exists() to return + // false, since it is useless for use then, anyway. + if ( @file_exists( "{$dir}\\{$fileName}.exe" ) ) + { + return "{$dir}\\{$fileName}.exe"; + } + } + } + // The @-operator is used here mainly to avoid open_basedir + // warnings. If open_basedir (or any other circumstance) + // prevents the desired file from being accessed, it is fine + // for file_exists() to return false, since it is useless for + // use then, anyway. + elseif ( @file_exists( "{$fileName}.exe" ) ) + { + return "{$fileName}.exe"; + } + break; + } + return null; + } + + /** + * Reset the cached information. + * + * @return void + * @access private + * @ignore + */ + public static function reset() + { + self::$imageIdentify = null; + self::$imageConvert = null; + self::$os = null; + } +} +?> diff --git a/library/ezc/Base/src/file.php b/library/ezc/Base/src/file.php index fb1de963b4..4f11aeb1ff 100644 --- a/library/ezc/Base/src/file.php +++ b/library/ezc/Base/src/file.php @@ -1,495 +1,495 @@ - - * - * - * - * @package Base - * @version //autogentag// - * @mainclass - */ -class ezcBaseFile -{ - /** - * This is the callback used by findRecursive to collect data. - * - * This callback method works together with walkRecursive() and is called - * for every file/and or directory. The $context is a callback specific - * container in which data can be stored and shared between the different - * calls to the callback function. The walkRecursive() function also passes - * in the full absolute directory in $sourceDir, the filename in $fileName - * and file information (such as size, modes, types) as an array as - * returned by PHP's stat() in the $fileInfo parameter. - * - * @param ezcBaseFileFindContext $context - * @param string $sourceDir - * @param string $fileName - * @param array(stat) $fileInfo - */ - static protected function findRecursiveCallback( ezcBaseFileFindContext $context, $sourceDir, $fileName, $fileInfo ) - { - // ignore if we have a directory - if ( $fileInfo['mode'] & 0x4000 ) - { - return; - } - - // update the statistics - $context->elements[] = $sourceDir . DIRECTORY_SEPARATOR . $fileName; - $context->count++; - $context->size += $fileInfo['size']; - } - - /** - * Walks files and directories recursively on a file system - * - * This method walks over a directory and calls a callback from every file - * and directory it finds. You can use $includeFilters to include only - * specific files, and $excludeFilters to exclude certain files from being - * returned. The function will always go into subdirectories even if the - * entry would not have passed the filters. - * - * The callback is passed in the $callback parameter, and the - * $callbackContext will be send to the callback function/method as - * parameter so that you can store data in there that persists with all the - * calls and recursive calls to this method. It's up to the callback method - * to do something useful with this. The callback function's parameters are - * in order: - * - * - * - * See {@see findRecursiveCallback()} for an example of a callback function. - * - * Filters are regular expressions and are therefore required to have - * starting and ending delimiters. The Perl Compatible syntax is used as - * regular expression language. - * - * @param string $sourceDir - * @param array(string) $includeFilters - * @param array(string) $excludeFilters - * @param callback $callback - * @param mixed $callbackContext - * - * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not - * a directory or does not exist. - * @throws ezcBaseFilePermissionException if the $sourceDir directory could - * not be opened for reading. - * @return array - */ - static public function walkRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), $callback, &$callbackContext ) - { - if ( !is_dir( $sourceDir ) ) - { - throw new ezcBaseFileNotFoundException( $sourceDir, 'directory' ); - } - $elements = array(); - $d = @dir( $sourceDir ); - if ( !$d ) - { - throw new ezcBaseFilePermissionException( $sourceDir, ezcBaseFileException::READ ); - } - - while ( ( $entry = $d->read() ) !== false ) - { - if ( $entry == '.' || $entry == '..' ) - { - continue; - } - - $fileInfo = @stat( $sourceDir . DIRECTORY_SEPARATOR . $entry ); - if ( !$fileInfo ) - { - $fileInfo = array( 'size' => 0, 'mode' => 0 ); - } - - if ( $fileInfo['mode'] & 0x4000 ) - { - // We need to ignore the Permission exceptions here as it can - // be normal that a directory can not be accessed. We only need - // the exception if the top directory could not be read. - try - { - call_user_func_array( $callback, array( $callbackContext, $sourceDir, $entry, $fileInfo ) ); - $subList = self::walkRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry, $includeFilters, $excludeFilters, $callback, $callbackContext ); - $elements = array_merge( $elements, $subList ); - } - catch ( ezcBaseFilePermissionException $e ) - { - } - } - else - { - // By default a file is included in the return list - $ok = true; - // Iterate over the $includeFilters and prohibit the file from - // being returned when atleast one of them does not match - foreach ( $includeFilters as $filter ) - { - if ( !preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) - { - $ok = false; - break; - } - } - // Iterate over the $excludeFilters and prohibit the file from - // being returns when atleast one of them matches - foreach ( $excludeFilters as $filter ) - { - if ( preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) - { - $ok = false; - break; - } - } - - // If everything's allright, call the callback and add the - // entry to the elements array - if ( $ok ) - { - call_user_func( $callback, $callbackContext, $sourceDir, $entry, $fileInfo ); - $elements[] = $sourceDir . DIRECTORY_SEPARATOR . $entry; - } - } - } - sort( $elements ); - return $elements; - } - - /** - * Finds files recursively on a file system - * - * With this method you can scan the file system for files. You can use - * $includeFilters to include only specific files, and $excludeFilters to - * exclude certain files from being returned. The function will always go - * into subdirectories even if the entry would not have passed the filters. - * It uses the {@see walkRecursive()} method to do the actually recursion. - * - * Filters are regular expressions and are therefore required to have - * starting and ending delimiters. The Perl Compatible syntax is used as - * regular expression language. - * - * If you pass an empty array to the $statistics argument, the function - * will in details about the number of files found into the 'count' array - * element, and the total filesize in the 'size' array element. Because this - * argument is passed by reference, you *have* to pass a variable and you - * can not pass a constant value such as "array()". - * - * @param string $sourceDir - * @param array(string) $includeFilters - * @param array(string) $excludeFilters - * @param array() $statistics - * - * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not - * a directory or does not exist. - * @throws ezcBaseFilePermissionException if the $sourceDir directory could - * not be opened for reading. - * @return array - */ - static public function findRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), &$statistics = null ) - { - // init statistics array - if ( !is_array( $statistics ) || !array_key_exists( 'size', $statistics ) || !array_key_exists( 'count', $statistics ) ) - { - $statistics['size'] = 0; - $statistics['count'] = 0; - } - - // create the context, and then start walking over the array - $context = new ezcBaseFileFindContext; - self::walkRecursive( $sourceDir, $includeFilters, $excludeFilters, array( 'ezcBaseFile', 'findRecursiveCallback' ), $context ); - - // collect the statistics - $statistics['size'] = $context->size; - $statistics['count'] = $context->count; - - // return the found and pattern-matched files - sort( $context->elements ); - return $context->elements; - } - - - /** - * Removes files and directories recursively from a file system - * - * This method recursively removes the $directory and all its contents. - * You should be extremely careful with this method as it has the - * potential to erase everything that the current user has access to. - * - * @param string $directory - */ - static public function removeRecursive( $directory ) - { - $sourceDir = realpath( $directory ); - if ( !$sourceDir ) - { - throw new ezcBaseFileNotFoundException( $directory, 'directory' ); - } - $d = @dir( $sourceDir ); - if ( !$d ) - { - throw new ezcBaseFilePermissionException( $directory, ezcBaseFileException::READ ); - } - // check if we can remove the dir - $parentDir = realpath( $directory . DIRECTORY_SEPARATOR . '..' ); - if ( !is_writable( $parentDir ) ) - { - throw new ezcBaseFilePermissionException( $parentDir, ezcBaseFileException::WRITE ); - } - // loop over contents - while ( ( $entry = $d->read() ) !== false ) - { - if ( $entry == '.' || $entry == '..' ) - { - continue; - } - - if ( is_dir( $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) - { - self::removeRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry ); - } - else - { - if ( @unlink( $sourceDir . DIRECTORY_SEPARATOR . $entry ) === false ) - { - throw new ezcBaseFilePermissionException( $directory . DIRECTORY_SEPARATOR . $entry, ezcBaseFileException::REMOVE ); - } - } - } - $d->close(); - rmdir( $sourceDir ); - } - - /** - * Recursively copy a file or directory. - * - * Recursively copy a file or directory in $source to the given - * destination. If a depth is given, the operation will stop, if the given - * recursion depth is reached. A depth of -1 means no limit, while a depth - * of 0 means, that only the current file or directory will be copied, - * without any recursion. - * - * You may optionally define modes used to create files and directories. - * - * @throws ezcBaseFileNotFoundException - * If the $sourceDir directory is not a directory or does not exist. - * @throws ezcBaseFilePermissionException - * If the $sourceDir directory could not be opened for reading, or the - * destination is not writeable. - * - * @param string $source - * @param string $destination - * @param int $depth - * @param int $dirMode - * @param int $fileMode - * @return void - */ - static public function copyRecursive( $source, $destination, $depth = -1, $dirMode = 0775, $fileMode = 0664 ) - { - // Check if source file exists at all. - if ( !is_file( $source ) && !is_dir( $source ) ) - { - throw new ezcBaseFileNotFoundException( $source ); - } - - // Destination file should NOT exist - if ( is_file( $destination ) || is_dir( $destination ) ) - { - throw new ezcBaseFilePermissionException( $destination, ezcBaseFileException::WRITE ); - } - - // Skip non readable files in source directory - if ( !is_readable( $source ) ) - { - return; - } - - // Copy - if ( is_dir( $source ) ) - { - mkdir( $destination ); - // To ignore umask, umask() should not be changed with - // multithreaded servers... - chmod( $destination, $dirMode ); - } - elseif ( is_file( $source ) ) - { - copy( $source, $destination ); - chmod( $destination, $fileMode ); - } - - if ( ( $depth === 0 ) || - ( !is_dir( $source ) ) ) - { - // Do not recurse (any more) - return; - } - - // Recurse - $dh = opendir( $source ); - while ( ( $file = readdir( $dh ) ) !== false ) - { - if ( ( $file === '.' ) || - ( $file === '..' ) ) - { - continue; - } - - self::copyRecursive( - $source . '/' . $file, - $destination . '/' . $file, - $depth - 1, $dirMode, $fileMode - ); - } - } - - /** - * Calculates the relative path of the file/directory '$path' to a given - * $base path. - * - * $path and $base should be fully absolute paths. This function returns the - * answer of "How do I go from $base to $path". If the $path and $base are - * the same path, the function returns '.'. This method does not touch the - * filesystem. - * - * @param string $path - * @param string $base - * @return string - */ - static public function calculateRelativePath( $path, $base ) - { - // Sanitize the paths to use the correct directory separator for the platform - $path = strtr( $path, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); - $base = strtr( $base, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); - - $base = explode( DIRECTORY_SEPARATOR, $base ); - $path = explode( DIRECTORY_SEPARATOR, $path ); - - // If the paths are the same we return - if ( $base === $path ) - { - return '.'; - } - - $result = ''; - - $pathPart = array_shift( $path ); - $basePart = array_shift( $base ); - while ( $pathPart == $basePart ) - { - $pathPart = array_shift( $path ); - $basePart = array_shift( $base ); - } - - if ( $pathPart != null ) - { - array_unshift( $path, $pathPart ); - } - if ( $basePart != null ) - { - array_unshift( $base, $basePart ); - } - - $result = str_repeat( '..' . DIRECTORY_SEPARATOR, count( $base ) ); - // prevent a trailing DIRECTORY_SEPARATOR in case there is only a .. - if ( count( $path ) == 0 ) - { - $result = substr( $result, 0, -strlen( DIRECTORY_SEPARATOR ) ); - } - $result .= join( DIRECTORY_SEPARATOR, $path ); - - return $result; - } - - /** - * Returns whether the passed $path is an absolute path, giving the current $os. - * - * With the $os parameter you can tell this function to use the semantics - * for a different operating system to determine whether a path is - * absolute. The $os argument defaults to the OS that the script is running - * on. - * - * @param string $path - * @param string $os - * @return bool - */ - public static function isAbsolutePath( $path, $os = null ) - { - if ( $os === null ) - { - $os = ezcBaseFeatures::os(); - } - - // Stream wrapper like phar can also be considered absolute paths - if ( preg_match( '(^[a-z]{3,}://)S', $path ) ) - { - return true; - } - - switch ( $os ) - { - case 'Windows': - // Sanitize the paths to use the correct directory separator for the platform - $path = strtr( $path, '\\/', '\\\\' ); - - // Absolute paths with drive letter: X:\ - if ( preg_match( '@^[A-Z]:\\\\@i', $path ) ) - { - return true; - } - - // Absolute paths with network paths: \\server\share\ - if ( preg_match( '@^\\\\\\\\[A-Z]+\\\\[^\\\\]@i', $path ) ) - { - return true; - } - break; - case 'Mac': - case 'Linux': - case 'FreeBSD': - default: - // Sanitize the paths to use the correct directory separator for the platform - $path = strtr( $path, '\\/', '//' ); - - if ( $path[0] == '/' ) - { - return true; - } - } - return false; - } -} -?> + + * + * + * + * @package Base + * @version 1.7 + * @mainclass + */ +class ezcBaseFile +{ + /** + * This is the callback used by findRecursive to collect data. + * + * This callback method works together with walkRecursive() and is called + * for every file/and or directory. The $context is a callback specific + * container in which data can be stored and shared between the different + * calls to the callback function. The walkRecursive() function also passes + * in the full absolute directory in $sourceDir, the filename in $fileName + * and file information (such as size, modes, types) as an array as + * returned by PHP's stat() in the $fileInfo parameter. + * + * @param ezcBaseFileFindContext $context + * @param string $sourceDir + * @param string $fileName + * @param array(stat) $fileInfo + */ + static protected function findRecursiveCallback( ezcBaseFileFindContext $context, $sourceDir, $fileName, $fileInfo ) + { + // ignore if we have a directory + if ( $fileInfo['mode'] & 0x4000 ) + { + return; + } + + // update the statistics + $context->elements[] = $sourceDir . DIRECTORY_SEPARATOR . $fileName; + $context->count++; + $context->size += $fileInfo['size']; + } + + /** + * Walks files and directories recursively on a file system + * + * This method walks over a directory and calls a callback from every file + * and directory it finds. You can use $includeFilters to include only + * specific files, and $excludeFilters to exclude certain files from being + * returned. The function will always go into subdirectories even if the + * entry would not have passed the filters. + * + * The callback is passed in the $callback parameter, and the + * $callbackContext will be send to the callback function/method as + * parameter so that you can store data in there that persists with all the + * calls and recursive calls to this method. It's up to the callback method + * to do something useful with this. The callback function's parameters are + * in order: + * + * + * + * See {@see findRecursiveCallback()} for an example of a callback function. + * + * Filters are regular expressions and are therefore required to have + * starting and ending delimiters. The Perl Compatible syntax is used as + * regular expression language. + * + * @param string $sourceDir + * @param array(string) $includeFilters + * @param array(string) $excludeFilters + * @param callback $callback + * @param mixed $callbackContext + * + * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not + * a directory or does not exist. + * @throws ezcBaseFilePermissionException if the $sourceDir directory could + * not be opened for reading. + * @return array + */ + static public function walkRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), $callback, &$callbackContext ) + { + if ( !is_dir( $sourceDir ) ) + { + throw new ezcBaseFileNotFoundException( $sourceDir, 'directory' ); + } + $elements = array(); + $d = @dir( $sourceDir ); + if ( !$d ) + { + throw new ezcBaseFilePermissionException( $sourceDir, ezcBaseFileException::READ ); + } + + while ( ( $entry = $d->read() ) !== false ) + { + if ( $entry == '.' || $entry == '..' ) + { + continue; + } + + $fileInfo = @stat( $sourceDir . DIRECTORY_SEPARATOR . $entry ); + if ( !$fileInfo ) + { + $fileInfo = array( 'size' => 0, 'mode' => 0 ); + } + + if ( $fileInfo['mode'] & 0x4000 ) + { + // We need to ignore the Permission exceptions here as it can + // be normal that a directory can not be accessed. We only need + // the exception if the top directory could not be read. + try + { + call_user_func_array( $callback, array( $callbackContext, $sourceDir, $entry, $fileInfo ) ); + $subList = self::walkRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry, $includeFilters, $excludeFilters, $callback, $callbackContext ); + $elements = array_merge( $elements, $subList ); + } + catch ( ezcBaseFilePermissionException $e ) + { + } + } + else + { + // By default a file is included in the return list + $ok = true; + // Iterate over the $includeFilters and prohibit the file from + // being returned when atleast one of them does not match + foreach ( $includeFilters as $filter ) + { + if ( !preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) + { + $ok = false; + break; + } + } + // Iterate over the $excludeFilters and prohibit the file from + // being returns when atleast one of them matches + foreach ( $excludeFilters as $filter ) + { + if ( preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) + { + $ok = false; + break; + } + } + + // If everything's allright, call the callback and add the + // entry to the elements array + if ( $ok ) + { + call_user_func( $callback, $callbackContext, $sourceDir, $entry, $fileInfo ); + $elements[] = $sourceDir . DIRECTORY_SEPARATOR . $entry; + } + } + } + sort( $elements ); + return $elements; + } + + /** + * Finds files recursively on a file system + * + * With this method you can scan the file system for files. You can use + * $includeFilters to include only specific files, and $excludeFilters to + * exclude certain files from being returned. The function will always go + * into subdirectories even if the entry would not have passed the filters. + * It uses the {@see walkRecursive()} method to do the actually recursion. + * + * Filters are regular expressions and are therefore required to have + * starting and ending delimiters. The Perl Compatible syntax is used as + * regular expression language. + * + * If you pass an empty array to the $statistics argument, the function + * will in details about the number of files found into the 'count' array + * element, and the total filesize in the 'size' array element. Because this + * argument is passed by reference, you *have* to pass a variable and you + * can not pass a constant value such as "array()". + * + * @param string $sourceDir + * @param array(string) $includeFilters + * @param array(string) $excludeFilters + * @param array() $statistics + * + * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not + * a directory or does not exist. + * @throws ezcBaseFilePermissionException if the $sourceDir directory could + * not be opened for reading. + * @return array + */ + static public function findRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), &$statistics = null ) + { + // init statistics array + if ( !is_array( $statistics ) || !array_key_exists( 'size', $statistics ) || !array_key_exists( 'count', $statistics ) ) + { + $statistics['size'] = 0; + $statistics['count'] = 0; + } + + // create the context, and then start walking over the array + $context = new ezcBaseFileFindContext; + self::walkRecursive( $sourceDir, $includeFilters, $excludeFilters, array( 'ezcBaseFile', 'findRecursiveCallback' ), $context ); + + // collect the statistics + $statistics['size'] = $context->size; + $statistics['count'] = $context->count; + + // return the found and pattern-matched files + sort( $context->elements ); + return $context->elements; + } + + + /** + * Removes files and directories recursively from a file system + * + * This method recursively removes the $directory and all its contents. + * You should be extremely careful with this method as it has the + * potential to erase everything that the current user has access to. + * + * @param string $directory + */ + static public function removeRecursive( $directory ) + { + $sourceDir = realpath( $directory ); + if ( !$sourceDir ) + { + throw new ezcBaseFileNotFoundException( $directory, 'directory' ); + } + $d = @dir( $sourceDir ); + if ( !$d ) + { + throw new ezcBaseFilePermissionException( $directory, ezcBaseFileException::READ ); + } + // check if we can remove the dir + $parentDir = realpath( $directory . DIRECTORY_SEPARATOR . '..' ); + if ( !is_writable( $parentDir ) ) + { + throw new ezcBaseFilePermissionException( $parentDir, ezcBaseFileException::WRITE ); + } + // loop over contents + while ( ( $entry = $d->read() ) !== false ) + { + if ( $entry == '.' || $entry == '..' ) + { + continue; + } + + if ( is_dir( $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) + { + self::removeRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry ); + } + else + { + if ( @unlink( $sourceDir . DIRECTORY_SEPARATOR . $entry ) === false ) + { + throw new ezcBaseFilePermissionException( $directory . DIRECTORY_SEPARATOR . $entry, ezcBaseFileException::REMOVE ); + } + } + } + $d->close(); + rmdir( $sourceDir ); + } + + /** + * Recursively copy a file or directory. + * + * Recursively copy a file or directory in $source to the given + * destination. If a depth is given, the operation will stop, if the given + * recursion depth is reached. A depth of -1 means no limit, while a depth + * of 0 means, that only the current file or directory will be copied, + * without any recursion. + * + * You may optionally define modes used to create files and directories. + * + * @throws ezcBaseFileNotFoundException + * If the $sourceDir directory is not a directory or does not exist. + * @throws ezcBaseFilePermissionException + * If the $sourceDir directory could not be opened for reading, or the + * destination is not writeable. + * + * @param string $source + * @param string $destination + * @param int $depth + * @param int $dirMode + * @param int $fileMode + * @return void + */ + static public function copyRecursive( $source, $destination, $depth = -1, $dirMode = 0775, $fileMode = 0664 ) + { + // Check if source file exists at all. + if ( !is_file( $source ) && !is_dir( $source ) ) + { + throw new ezcBaseFileNotFoundException( $source ); + } + + // Destination file should NOT exist + if ( is_file( $destination ) || is_dir( $destination ) ) + { + throw new ezcBaseFilePermissionException( $destination, ezcBaseFileException::WRITE ); + } + + // Skip non readable files in source directory + if ( !is_readable( $source ) ) + { + return; + } + + // Copy + if ( is_dir( $source ) ) + { + mkdir( $destination ); + // To ignore umask, umask() should not be changed with + // multithreaded servers... + chmod( $destination, $dirMode ); + } + elseif ( is_file( $source ) ) + { + copy( $source, $destination ); + chmod( $destination, $fileMode ); + } + + if ( ( $depth === 0 ) || + ( !is_dir( $source ) ) ) + { + // Do not recurse (any more) + return; + } + + // Recurse + $dh = opendir( $source ); + while ( ( $file = readdir( $dh ) ) !== false ) + { + if ( ( $file === '.' ) || + ( $file === '..' ) ) + { + continue; + } + + self::copyRecursive( + $source . '/' . $file, + $destination . '/' . $file, + $depth - 1, $dirMode, $fileMode + ); + } + } + + /** + * Calculates the relative path of the file/directory '$path' to a given + * $base path. + * + * $path and $base should be fully absolute paths. This function returns the + * answer of "How do I go from $base to $path". If the $path and $base are + * the same path, the function returns '.'. This method does not touch the + * filesystem. + * + * @param string $path + * @param string $base + * @return string + */ + static public function calculateRelativePath( $path, $base ) + { + // Sanitize the paths to use the correct directory separator for the platform + $path = strtr( $path, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); + $base = strtr( $base, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); + + $base = explode( DIRECTORY_SEPARATOR, $base ); + $path = explode( DIRECTORY_SEPARATOR, $path ); + + // If the paths are the same we return + if ( $base === $path ) + { + return '.'; + } + + $result = ''; + + $pathPart = array_shift( $path ); + $basePart = array_shift( $base ); + while ( $pathPart == $basePart ) + { + $pathPart = array_shift( $path ); + $basePart = array_shift( $base ); + } + + if ( $pathPart != null ) + { + array_unshift( $path, $pathPart ); + } + if ( $basePart != null ) + { + array_unshift( $base, $basePart ); + } + + $result = str_repeat( '..' . DIRECTORY_SEPARATOR, count( $base ) ); + // prevent a trailing DIRECTORY_SEPARATOR in case there is only a .. + if ( count( $path ) == 0 ) + { + $result = substr( $result, 0, -strlen( DIRECTORY_SEPARATOR ) ); + } + $result .= join( DIRECTORY_SEPARATOR, $path ); + + return $result; + } + + /** + * Returns whether the passed $path is an absolute path, giving the current $os. + * + * With the $os parameter you can tell this function to use the semantics + * for a different operating system to determine whether a path is + * absolute. The $os argument defaults to the OS that the script is running + * on. + * + * @param string $path + * @param string $os + * @return bool + */ + public static function isAbsolutePath( $path, $os = null ) + { + if ( $os === null ) + { + $os = ezcBaseFeatures::os(); + } + + // Stream wrapper like phar can also be considered absolute paths + if ( preg_match( '(^[a-z]{3,}://)S', $path ) ) + { + return true; + } + + switch ( $os ) + { + case 'Windows': + // Sanitize the paths to use the correct directory separator for the platform + $path = strtr( $path, '\\/', '\\\\' ); + + // Absolute paths with drive letter: X:\ + if ( preg_match( '@^[A-Z]:\\\\@i', $path ) ) + { + return true; + } + + // Absolute paths with network paths: \\server\share\ + if ( preg_match( '@^\\\\\\\\[A-Z]+\\\\[^\\\\]@i', $path ) ) + { + return true; + } + break; + case 'Mac': + case 'Linux': + case 'FreeBSD': + default: + // Sanitize the paths to use the correct directory separator for the platform + $path = strtr( $path, '\\/', '//' ); + + if ( $path[0] == '/' ) + { + return true; + } + } + return false; + } +} +?> diff --git a/library/ezc/Base/src/init.php b/library/ezc/Base/src/init.php index ce975c8c6b..5ce24358f9 100644 --- a/library/ezc/Base/src/init.php +++ b/library/ezc/Base/src/init.php @@ -1,125 +1,125 @@ - - * - * - * - * You will also need to configure which callback class to call. This you do - * with the ezcBaseInit::setCallback() method. The following examples sets the - * callback classname for the configuration identifier - * 'ezcInitConfigurationManager' to 'cfgConfigurationManager': - * - * - * - * - * - * The class 'cfgConfigurationManager' is required to implement the - * ezcBaseConfigurationInitializer interface, which defines only one method: - * configureObject(). An example on how to implement such a class could be: - * - * - * init( 'ezcConfigurationIniReader', 'settings', array( 'useComments' => true ) ); - * } - * } - * ?> - * - * - * Of course the implementation of this callback class is up to the application - * developer that uses the component (in this example the Configuration - * component's class ezcConfigurationManager). - * - * @package Base - * @version //autogentag// - */ -class ezcBaseInit -{ - /** - * Contains the callback where the identifier is the key of the array, and the classname to callback to the value. - * - * @var array(string=>string) - */ - static private $callbackMap = array(); - - /** - * Adds the classname $callbackClassname as callback for the identifier $identifier. - * - * @param string $identifier - * @param string $callbackClassname - */ - public static function setCallback( $identifier, $callbackClassname ) - { - if ( array_key_exists( $identifier, self::$callbackMap ) ) - { - throw new ezcBaseInitCallbackConfiguredException( $identifier, self::$callbackMap[$identifier] ); - } - else - { - // Check if the passed classname actually exists - if ( !ezcBaseFeatures::classExists( $callbackClassname, true ) ) - { - throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname ); - } - - // Check if the passed classname actually implements the interface. - if ( !in_array( 'ezcBaseConfigurationInitializer', class_implements( $callbackClassname ) ) ) - { - throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname ); - } - - self::$callbackMap[$identifier] = $callbackClassname; - } - } - - /** - * Uses the configured callback belonging to $identifier to configure the $object. - * - * The method will return the return value of the callback method, or null - * in case there was no callback set for the specified $identifier. - * - * @param string $identifier - * @param object $object - * @return mixed - */ - public static function fetchConfig( $identifier, $object ) - { - if ( isset( self::$callbackMap[$identifier] ) ) - { - $callbackClassname = self::$callbackMap[$identifier]; - return call_user_func( array( $callbackClassname, 'configureObject' ), $object ); - } - return null; - } -} -?> + + * + * + * + * You will also need to configure which callback class to call. This you do + * with the ezcBaseInit::setCallback() method. The following examples sets the + * callback classname for the configuration identifier + * 'ezcInitConfigurationManager' to 'cfgConfigurationManager': + * + * + * + * + * + * The class 'cfgConfigurationManager' is required to implement the + * ezcBaseConfigurationInitializer interface, which defines only one method: + * configureObject(). An example on how to implement such a class could be: + * + * + * init( 'ezcConfigurationIniReader', 'settings', array( 'useComments' => true ) ); + * } + * } + * ?> + * + * + * Of course the implementation of this callback class is up to the application + * developer that uses the component (in this example the Configuration + * component's class ezcConfigurationManager). + * + * @package Base + * @version 1.7 + */ +class ezcBaseInit +{ + /** + * Contains the callback where the identifier is the key of the array, and the classname to callback to the value. + * + * @var array(string=>string) + */ + static private $callbackMap = array(); + + /** + * Adds the classname $callbackClassname as callback for the identifier $identifier. + * + * @param string $identifier + * @param string $callbackClassname + */ + public static function setCallback( $identifier, $callbackClassname ) + { + if ( array_key_exists( $identifier, self::$callbackMap ) ) + { + throw new ezcBaseInitCallbackConfiguredException( $identifier, self::$callbackMap[$identifier] ); + } + else + { + // Check if the passed classname actually exists + if ( !ezcBaseFeatures::classExists( $callbackClassname, true ) ) + { + throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname ); + } + + // Check if the passed classname actually implements the interface. + if ( !in_array( 'ezcBaseConfigurationInitializer', class_implements( $callbackClassname ) ) ) + { + throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname ); + } + + self::$callbackMap[$identifier] = $callbackClassname; + } + } + + /** + * Uses the configured callback belonging to $identifier to configure the $object. + * + * The method will return the return value of the callback method, or null + * in case there was no callback set for the specified $identifier. + * + * @param string $identifier + * @param object $object + * @return mixed + */ + public static function fetchConfig( $identifier, $object ) + { + if ( isset( self::$callbackMap[$identifier] ) ) + { + $callbackClassname = self::$callbackMap[$identifier]; + return call_user_func( array( $callbackClassname, 'configureObject' ), $object ); + } + return null; + } +} +?> diff --git a/library/ezc/Base/src/interfaces/configuration_initializer.php b/library/ezc/Base/src/interfaces/configuration_initializer.php index 34fdcea550..0388027f36 100644 --- a/library/ezc/Base/src/interfaces/configuration_initializer.php +++ b/library/ezc/Base/src/interfaces/configuration_initializer.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Base/src/interfaces/persistable.php b/library/ezc/Base/src/interfaces/persistable.php index 9daf1f48ff..3da6ce89d6 100644 --- a/library/ezc/Base/src/interfaces/persistable.php +++ b/library/ezc/Base/src/interfaces/persistable.php @@ -1,40 +1,40 @@ -mixed) - */ - public function getState(); - - /** - * Accepts an array containing data for one or more of the class' properties. - * - * @param array $properties - */ - public function setState( array $properties ); -} -?> +mixed) + */ + public function getState(); + + /** + * Accepts an array containing data for one or more of the class' properties. + * + * @param array $properties + */ + public function setState( array $properties ); +} +?> diff --git a/library/ezc/Base/src/metadata.php b/library/ezc/Base/src/metadata.php index cfa37e92e1..3962649ce8 100644 --- a/library/ezc/Base/src/metadata.php +++ b/library/ezc/Base/src/metadata.php @@ -1,113 +1,120 @@ -reader = new ezcBaseMetaDataTarballReader; - break; - case 'pear': - $this->reader = new ezcBaseMetaDataPearReader; - break; - default: - throw new ezcBaseMetaDataReaderException( "Unknown install method '$installMethod'." ); - break; - } - } - - /** - * Returns the version string for the installed eZ Components bundle. - * - * A version string such as "2008.2.2" is returned. - * - * @return string - */ - public function getBundleVersion() - { - return $this->reader->getBundleVersion(); - } - - /** - * Returns a PHP version string that describes the required PHP version for - * this installed eZ Components bundle. - * - * @return string - */ - public function getRequiredPhpVersion() - { - return $this->reader->getRequiredPhpVersion(); - } - - /** - * Returns whether $componentName is installed - * - * If installed with PEAR, it checks the PEAR registry whether the - * component is there. In case the tarball installation method is used, it - * will return true for every component that exists (because all of them - * are then available). - * - * @return bool - */ - public function isComponentInstalled( $componentName ) - { - return $this->reader->isComponentInstalled( $componentName ); - } - - /** - * Returns the version string of the available $componentName or false when - * the component is not installed. - * - * @return string - */ - public function getComponentVersion( $componentName ) - { - return $this->reader->getComponentVersion( $componentName ); - } - - /** - * Returns a list of components that $componentName depends on. - * - * If $componentName is left empty, all installed components are returned. - * - * The returned array has as keys the component names, and as values the - * version of the components. - * - * @return array(string=>string). - */ - public function getComponentDependencies( $componentName = null ) - { - return call_user_func_array( array( $this->reader, 'getComponentDependencies' ), $componentName !== null ? array( $componentName ) : array() ); - } -} -?> +reader = new ezcBaseMetaDataTarballReader; + break; + case 'pear': + $this->reader = new ezcBaseMetaDataPearReader; + break; + default: + throw new ezcBaseMetaDataReaderException( "Unknown install method '$installMethod'." ); + break; + } + } + + /** + * Returns the version string for the installed eZ Components bundle. + * + * A version string such as "2008.2.2" is returned. + * + * @return string + */ + public function getBundleVersion() + { + return $this->reader->getBundleVersion(); + } + + /** + * Returns a PHP version string that describes the required PHP version for + * this installed eZ Components bundle. + * + * @return string + */ + public function getRequiredPhpVersion() + { + return $this->reader->getRequiredPhpVersion(); + } + + /** + * Returns whether $componentName is installed + * + * If installed with PEAR, it checks the PEAR registry whether the + * component is there. In case the tarball installation method is used, it + * will return true for every component that exists (because all of them + * are then available). + * + * @return bool + */ + public function isComponentInstalled( $componentName ) + { + return $this->reader->isComponentInstalled( $componentName ); + } + + /** + * Returns the version string of the available $componentName or false when + * the component is not installed. + * + * @return string + */ + public function getComponentVersion( $componentName ) + { + return $this->reader->getComponentVersion( $componentName ); + } + + /** + * Returns a list of components that $componentName depends on. + * + * If $componentName is left empty, all installed components are returned. + * + * The returned array has as keys the component names, and as values the + * version of the components. + * + * @return array(string=>string). + */ + public function getComponentDependencies( $componentName = null ) + { + if ( $componentName === null ) + { + return $this->reader->getComponentDependencies(); + } + else + { + return $this->reader->getComponentDependencies( $componentName ); + } + } +} +?> diff --git a/library/ezc/Base/src/metadata/pear.php b/library/ezc/Base/src/metadata/pear.php index 78fe7fd93a..6c789d29fa 100644 --- a/library/ezc/Base/src/metadata/pear.php +++ b/library/ezc/Base/src/metadata/pear.php @@ -1,122 +1,129 @@ -registry = new PEAR_Registry; - } - - /** - * Returns the version string for the installed eZ Components bundle. - * - * A version string such as "2008.2.2" is returned. - * - * @return string - */ - public function getBundleVersion() - { - @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' ); - return $packageInfo['version']['release']; - } - - /** - * Returns a PHP version string that describes the required PHP version for - * this installed eZ Components bundle. - * - * @return string - */ - public function getRequiredPhpVersion() - { - @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' ); - return $packageInfo['dependencies']['php']['min']; - } - - /** - * Returns whether $componentName is installed - * - * Checks the PEAR registry whether the component is there. - * - * @return bool - */ - public function isComponentInstalled( $componentName ) - { - @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' ); - return is_array( $packageInfo ); - } - - /** - * Returns the version string of the available $componentName or false when - * the component is not installed. - * - * @return string - */ - public function getComponentVersion( $componentName ) - { - @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' ); - $release = $packageInfo['version']['release']; - return $release === null ? false : $release; - } - - /** - * Returns a list of components that $componentName depends on. - * - * If $componentName is left empty, all installed components are returned. - * - * The returned array has as keys the component names, and as values the - * version of the components. - * - * @return array(string=>string). - */ - public function getComponentDependencies( $componentName = 'ezcomponents' ) - { - @$packageInfo = $this->registry->packageInfo( $componentName, 'dependencies', 'components.ez.no' ); - if ( isset( $packageInfo['required']['package'] ) ) - { - $deps = array(); - if ( isset( $packageInfo['required']['package']['name'] ) ) - { - $deps[$packageInfo['required']['package']['name']] = $packageInfo['required']['package']['min']; - } - else - { - foreach ( $packageInfo['required']['package'] as $package ) - { - $deps[$package['name']] = $package['min']; - } - } - return $deps; - } - return array(); - } -} -?> +registry = new PEAR_Registry; + } + + /** + * Returns the version string for the installed eZ Components bundle. + * + * A version string such as "2008.2.2" is returned. + * + * @return string + */ + public function getBundleVersion() + { + @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' ); + return $packageInfo['version']['release']; + } + + /** + * Returns a PHP version string that describes the required PHP version for + * this installed eZ Components bundle. + * + * @return string + */ + public function getRequiredPhpVersion() + { + @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' ); + if ( array_key_exists( 'required', $packageInfo['dependencies'] ) ) + { + return $packageInfo['dependencies']['required']['php']['min']; + } + return $packageInfo['dependencies']['php']['min']; + } + + /** + * Returns whether $componentName is installed + * + * Checks the PEAR registry whether the component is there. + * + * @return bool + */ + public function isComponentInstalled( $componentName ) + { + @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' ); + return is_array( $packageInfo ); + } + + /** + * Returns the version string of the available $componentName or false when + * the component is not installed. + * + * @return string + */ + public function getComponentVersion( $componentName ) + { + @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' ); + $release = $packageInfo['version']['release']; + return $release === null ? false : $release; + } + + /** + * Returns a list of components that $componentName depends on. + * + * If $componentName is left empty, all installed components are returned. + * + * The returned array has as keys the component names, and as values the + * version of the components. + * + * @return array(string=>string). + */ + public function getComponentDependencies( $componentName = 'ezcomponents' ) + { + @$packageInfo = $this->registry->packageInfo( $componentName, 'dependencies', 'components.ez.no' ); + if ( isset( $packageInfo['required']['package'] ) ) + { + $deps = array(); + if ( isset( $packageInfo['required']['package']['name'] ) ) + { + $deps[$packageInfo['required']['package']['name']] = $packageInfo['required']['package']['min']; + } + else + { + foreach ( $packageInfo['required']['package'] as $package ) + { + $deps[$package['name']] = $package['min']; + } + } + return $deps; + } + return array(); + } +} +?> diff --git a/library/ezc/Base/src/metadata/tarball.php b/library/ezc/Base/src/metadata/tarball.php index 49fb68ccc8..5f82042ee2 100644 --- a/library/ezc/Base/src/metadata/tarball.php +++ b/library/ezc/Base/src/metadata/tarball.php @@ -1,153 +1,153 @@ -xml = simplexml_load_file( $filename ); - } - - /** - * Returns the version string for the installed eZ Components bundle. - * - * A version string such as "2008.2.2" is returned. - * - * @return string - */ - public function getBundleVersion() - { - return (string) $this->xml->version; - } - - /** - * Returns a PHP version string that describes the required PHP version for - * this installed eZ Components bundle. - * - * @return string - */ - public function getRequiredPhpVersion() - { - return (string) $this->xml->deps->php; - } - - /** - * Returns whether $componentName is installed - * - * Returns true for every component that exists (because all of them are - * then available). - * - * @return bool - */ - public function isComponentInstalled( $componentName ) - { - $root = $this->xml->deps->packages->package; - - foreach ( $root as $package ) - { - if ( (string) $package['name'] == $componentName ) - { - return true; - } - } - return false; - } - - /** - * Returns the version string of the available $componentName or false when - * the component is not installed. - * - * @return string - */ - public function getComponentVersion( $componentName ) - { - $root = $this->xml->deps->packages->package; - - foreach ( $root as $package ) - { - if ( (string) $package['name'] == $componentName ) - { - return (string) $package['version']; - } - } - return false; - } - - /** - * Returns a list of components that $componentName depends on. - * - * If $componentName is left empty, all installed components are returned. - * - * The returned array has as keys the component names, and as values the - * version of the components. It returns null of the $componentName - * is not found. - * - * @return array(string=>string). - */ - public function getComponentDependencies( $componentName = null ) - { - $baseVersion = false; - $root = $this->xml->deps->packages; - $found = $componentName === null ? true : false; - - // in case $componentName != null, we loop through all the components - // in the file, and figure out the new root that we can list dependency - // packages from. - foreach ( $root->package as $package ) - { - if ( (string) $package['name'] == 'Base' ) - { - $baseVersion = $package['version']; - } - if ( !$found && (string) $package['name'] == $componentName ) - { - $root = $package->deps; - $found = true; - } - } - - if ( !$found ) - { - return null; - } - - // We always add the Base dependency even though it's not in the dependency file. - $deps = array(); - $deps['Base'] = (string) $baseVersion; - - if ( !isset( $root->package ) ) - { - return $deps; - } - foreach ( $root->package as $package ) - { - $deps[(string) $package['name']] = (string) $package['version']; - } - return $deps; - } -} -?> +xml = simplexml_load_file( $filename ); + } + + /** + * Returns the version string for the installed eZ Components bundle. + * + * A version string such as "2008.2.2" is returned. + * + * @return string + */ + public function getBundleVersion() + { + return (string) $this->xml->version; + } + + /** + * Returns a PHP version string that describes the required PHP version for + * this installed eZ Components bundle. + * + * @return string + */ + public function getRequiredPhpVersion() + { + return (string) $this->xml->deps->php; + } + + /** + * Returns whether $componentName is installed + * + * Returns true for every component that exists (because all of them are + * then available). + * + * @return bool + */ + public function isComponentInstalled( $componentName ) + { + $root = $this->xml->deps->packages->package; + + foreach ( $root as $package ) + { + if ( (string) $package['name'] == $componentName ) + { + return true; + } + } + return false; + } + + /** + * Returns the version string of the available $componentName or false when + * the component is not installed. + * + * @return string + */ + public function getComponentVersion( $componentName ) + { + $root = $this->xml->deps->packages->package; + + foreach ( $root as $package ) + { + if ( (string) $package['name'] == $componentName ) + { + return (string) $package['version']; + } + } + return false; + } + + /** + * Returns a list of components that $componentName depends on. + * + * If $componentName is left empty, all installed components are returned. + * + * The returned array has as keys the component names, and as values the + * version of the components. It returns null of the $componentName + * is not found. + * + * @return array(string=>string). + */ + public function getComponentDependencies( $componentName = null ) + { + $baseVersion = false; + $root = $this->xml->deps->packages; + $found = $componentName === null ? true : false; + + // in case $componentName != null, we loop through all the components + // in the file, and figure out the new root that we can list dependency + // packages from. + foreach ( $root->package as $package ) + { + if ( (string) $package['name'] == 'Base' ) + { + $baseVersion = $package['version']; + } + if ( !$found && (string) $package['name'] == $componentName ) + { + $root = $package->deps; + $found = true; + } + } + + if ( !$found ) + { + return null; + } + + // We always add the Base dependency even though it's not in the dependency file. + $deps = array(); + $deps['Base'] = (string) $baseVersion; + + if ( !isset( $root->package ) ) + { + return $deps; + } + foreach ( $root->package as $package ) + { + $deps[(string) $package['name']] = (string) $package['version']; + } + return $deps; + } +} +?> diff --git a/library/ezc/Base/src/options.php b/library/ezc/Base/src/options.php index 2ec69456f7..79cc03c653 100644 --- a/library/ezc/Base/src/options.php +++ b/library/ezc/Base/src/options.php @@ -1,174 +1,174 @@ -mixed) - */ - protected $properties; - - /** - * Construct a new options object. - * Options are constructed from an option array by default. The constructor - * automatically passes the given options to the __set() method to set them - * in the class. - * - * @throws ezcBasePropertyNotFoundException - * If trying to access a non existent property. - * @throws ezcBaseValueException - * If the value for a property is out of range. - * @param array(string=>mixed) $options The initial options to set. - */ - public function __construct( array $options = array() ) - { - foreach ( $options as $option => $value ) - { - $this->__set( $option, $value ); - } - } - - /** - * Merge an array into the actual options object. - * This method merges an array of new options into the actual options object. - * - * @throws ezcBasePropertyNotFoundException - * If trying to access a non existent property. - * @throws ezcBaseValueException - * If the value for a property is out of range. - * @param array(string=>mixed) $newOptions The new options. - */ - public function merge( array $newOptions ) - { - foreach ( $newOptions as $key => $value ) - { - $this->__set( $key, $value ); - } - } - - /** - * Property get access. - * Simply returns a given option. - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @param string $propertyName The name of the option to get. - * @return mixed The option value. - * @ignore - * - * @throws ezcBasePropertyNotFoundException - * if the given property does not exist. - * @throws ezcBasePropertyPermissionException - * if the property to be set is a write-only property. - */ - public function __get( $propertyName ) - { - if ( $this->__isset( $propertyName ) === true ) - { - return $this->properties[$propertyName]; - } - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - - /** - * Sets an option. - * This method is called when an option is set. - * - * @param string $propertyName The name of the option to set. - * @param mixed $propertyValue The option value. - * @ignore - * - * @throws ezcBasePropertyNotFoundException - * if the given property does not exist. - * @throws ezcBaseValueException - * if the value to be assigned to a property is invalid. - * @throws ezcBasePropertyPermissionException - * if the property to be set is a read-only property. - */ - abstract public function __set( $propertyName, $propertyValue ); - - /** - * Returns if a option exists. - * - * @param string $propertyName Option name to check for. - * @return bool Whether the option exists. - * @ignore - */ - public function __isset( $propertyName ) - { - return array_key_exists( $propertyName, $this->properties ); - } - - /** - * Returns if an option exists. - * Allows isset() using ArrayAccess. - * - * @param string $propertyName The name of the option to get. - * @return bool Whether the option exists. - */ - public function offsetExists( $propertyName ) - { - return $this->__isset( $propertyName ); - } - - /** - * Returns an option value. - * Get an option value by ArrayAccess. - * - * @throws ezcBasePropertyNotFoundException - * If $propertyName is not a key in the $properties array. - * @param string $propertyName The name of the option to get. - * @return mixed The option value. - */ - public function offsetGet( $propertyName ) - { - return $this->__get( $propertyName ); - } - - /** - * Set an option. - * Sets an option using ArrayAccess. - * - * @throws ezcBasePropertyNotFoundException - * If $propertyName is not a key in the $properties array. - * @throws ezcBaseValueException - * If the value for a property is out of range. - * @param string $propertyName The name of the option to set. - * @param mixed $propertyValue The value for the option. - */ - public function offsetSet( $propertyName, $propertyValue ) - { - $this->__set( $propertyName, $propertyValue ); - } - - /** - * Unset an option. - * Unsets an option using ArrayAccess. - * - * @throws ezcBasePropertyNotFoundException - * If $propertyName is not a key in the $properties array. - * @throws ezcBaseValueException - * If a the value for a property is out of range. - * @param string $propertyName The name of the option to unset. - */ - public function offsetUnset( $propertyName ) - { - $this->__set( $propertyName, null ); - } -} -?> +mixed) + */ + protected $properties; + + /** + * Construct a new options object. + * Options are constructed from an option array by default. The constructor + * automatically passes the given options to the __set() method to set them + * in the class. + * + * @throws ezcBasePropertyNotFoundException + * If trying to access a non existent property. + * @throws ezcBaseValueException + * If the value for a property is out of range. + * @param array(string=>mixed) $options The initial options to set. + */ + public function __construct( array $options = array() ) + { + foreach ( $options as $option => $value ) + { + $this->__set( $option, $value ); + } + } + + /** + * Merge an array into the actual options object. + * This method merges an array of new options into the actual options object. + * + * @throws ezcBasePropertyNotFoundException + * If trying to access a non existent property. + * @throws ezcBaseValueException + * If the value for a property is out of range. + * @param array(string=>mixed) $newOptions The new options. + */ + public function merge( array $newOptions ) + { + foreach ( $newOptions as $key => $value ) + { + $this->__set( $key, $value ); + } + } + + /** + * Property get access. + * Simply returns a given option. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @param string $propertyName The name of the option to get. + * @return mixed The option value. + * @ignore + * + * @throws ezcBasePropertyNotFoundException + * if the given property does not exist. + * @throws ezcBasePropertyPermissionException + * if the property to be set is a write-only property. + */ + public function __get( $propertyName ) + { + if ( $this->__isset( $propertyName ) === true ) + { + return $this->properties[$propertyName]; + } + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + + /** + * Sets an option. + * This method is called when an option is set. + * + * @param string $propertyName The name of the option to set. + * @param mixed $propertyValue The option value. + * @ignore + * + * @throws ezcBasePropertyNotFoundException + * if the given property does not exist. + * @throws ezcBaseValueException + * if the value to be assigned to a property is invalid. + * @throws ezcBasePropertyPermissionException + * if the property to be set is a read-only property. + */ + abstract public function __set( $propertyName, $propertyValue ); + + /** + * Returns if a option exists. + * + * @param string $propertyName Option name to check for. + * @return bool Whether the option exists. + * @ignore + */ + public function __isset( $propertyName ) + { + return array_key_exists( $propertyName, $this->properties ); + } + + /** + * Returns if an option exists. + * Allows isset() using ArrayAccess. + * + * @param string $propertyName The name of the option to get. + * @return bool Whether the option exists. + */ + public function offsetExists( $propertyName ) + { + return $this->__isset( $propertyName ); + } + + /** + * Returns an option value. + * Get an option value by ArrayAccess. + * + * @throws ezcBasePropertyNotFoundException + * If $propertyName is not a key in the $properties array. + * @param string $propertyName The name of the option to get. + * @return mixed The option value. + */ + public function offsetGet( $propertyName ) + { + return $this->__get( $propertyName ); + } + + /** + * Set an option. + * Sets an option using ArrayAccess. + * + * @throws ezcBasePropertyNotFoundException + * If $propertyName is not a key in the $properties array. + * @throws ezcBaseValueException + * If the value for a property is out of range. + * @param string $propertyName The name of the option to set. + * @param mixed $propertyValue The value for the option. + */ + public function offsetSet( $propertyName, $propertyValue ) + { + $this->__set( $propertyName, $propertyValue ); + } + + /** + * Unset an option. + * Unsets an option using ArrayAccess. + * + * @throws ezcBasePropertyNotFoundException + * If $propertyName is not a key in the $properties array. + * @throws ezcBaseValueException + * If a the value for a property is out of range. + * @param string $propertyName The name of the option to unset. + */ + public function offsetUnset( $propertyName ) + { + $this->__set( $propertyName, null ); + } +} +?> diff --git a/library/ezc/Base/src/options/autoload.php b/library/ezc/Base/src/options/autoload.php index 90c1dd5ea0..09b758fa36 100644 --- a/library/ezc/Base/src/options/autoload.php +++ b/library/ezc/Base/src/options/autoload.php @@ -1,75 +1,75 @@ -mixed) $options - */ - public function __construct( array $options = array() ) - { - $this->preload = false; - $this->debug = false; - - parent::__construct( $options ); - } - - /** - * Sets the option $name to $value. - * - * @throws ezcBasePropertyNotFoundException - * if the property $name is not defined - * @throws ezcBaseValueException - * if $value is not correct for the property $name - * @param string $name - * @param mixed $value - * @ignore - */ - public function __set( $name, $value ) - { - switch ( $name ) - { - case 'debug': - case 'preload': - if ( !is_bool( $value ) ) - { - throw new ezcBaseValueException( $name, $value, 'bool' ); - } - $this->properties[$name] = $value; - break; - - default: - throw new ezcBasePropertyNotFoundException( $name ); - } - } -} -?> +mixed) $options + */ + public function __construct( array $options = array() ) + { + $this->preload = false; + $this->debug = false; + + parent::__construct( $options ); + } + + /** + * Sets the option $name to $value. + * + * @throws ezcBasePropertyNotFoundException + * if the property $name is not defined + * @throws ezcBaseValueException + * if $value is not correct for the property $name + * @param string $name + * @param mixed $value + * @ignore + */ + public function __set( $name, $value ) + { + switch ( $name ) + { + case 'debug': + case 'preload': + if ( !is_bool( $value ) ) + { + throw new ezcBaseValueException( $name, $value, 'bool' ); + } + $this->properties[$name] = $value; + break; + + default: + throw new ezcBasePropertyNotFoundException( $name ); + } + } +} +?> diff --git a/library/ezc/Base/src/struct.php b/library/ezc/Base/src/struct.php index a6df05461d..3c43f200ed 100644 --- a/library/ezc/Base/src/struct.php +++ b/library/ezc/Base/src/struct.php @@ -1,42 +1,42 @@ - + diff --git a/library/ezc/Base/src/structs/file_find_context.php b/library/ezc/Base/src/structs/file_find_context.php index 7f2b978568..1ce0cf19b7 100644 --- a/library/ezc/Base/src/structs/file_find_context.php +++ b/library/ezc/Base/src/structs/file_find_context.php @@ -1,72 +1,72 @@ -elements = $elements; - $this->count = $count; - $this->size = $size; - } - - /** - * Returns a new instance of this class with the data specified by $array. - * - * $array contains all the data members of this class in the form: - * array('member_name'=>value). - * - * __set_state makes this class exportable with var_export. - * var_export() generates code, that calls this method when it - * is parsed with PHP. - * - * @param array(string=>mixed) $array - * @return ezcBaseFileFindContext - */ - static public function __set_state( array $array ) - { - return new ezcBaseFileFindContext( $array['elements'], $array['count'], $array['size'] ); - } -} -?> +elements = $elements; + $this->count = $count; + $this->size = $size; + } + + /** + * Returns a new instance of this class with the data specified by $array. + * + * $array contains all the data members of this class in the form: + * array('member_name'=>value). + * + * __set_state makes this class exportable with var_export. + * var_export() generates code, that calls this method when it + * is parsed with PHP. + * + * @param array(string=>mixed) $array + * @return ezcBaseFileFindContext + */ + static public function __set_state( array $array ) + { + return new ezcBaseFileFindContext( $array['elements'], $array['count'], $array['size'] ); + } +} +?> diff --git a/library/ezc/Base/src/structs/repository_directory.php b/library/ezc/Base/src/structs/repository_directory.php index aac3575ff6..3c7b1935e2 100644 --- a/library/ezc/Base/src/structs/repository_directory.php +++ b/library/ezc/Base/src/structs/repository_directory.php @@ -1,83 +1,83 @@ -type = $type; - $this->basePath = $basePath; - $this->autoloadPath = $autoloadPath; - } - - /** - * Returns a new instance of this class with the data specified by $array. - * - * $array contains all the data members of this class in the form: - * array('member_name'=>value). - * - * __set_state makes this class exportable with var_export. - * var_export() generates code, that calls this method when it - * is parsed with PHP. - * - * @param array(string=>mixed) $array - * @return ezcBaseRepositoryDirectory - */ - static public function __set_state( array $array ) - { - return new ezcBaseRepositoryDirectory( $array['type'], $array['basePath'], $array['autoloadPath'] ); - } -} -?> +type = $type; + $this->basePath = $basePath; + $this->autoloadPath = $autoloadPath; + } + + /** + * Returns a new instance of this class with the data specified by $array. + * + * $array contains all the data members of this class in the form: + * array('member_name'=>value). + * + * __set_state makes this class exportable with var_export. + * var_export() generates code, that calls this method when it + * is parsed with PHP. + * + * @param array(string=>mixed) $array + * @return ezcBaseRepositoryDirectory + */ + static public function __set_state( array $array ) + { + return new ezcBaseRepositoryDirectory( $array['type'], $array['basePath'], $array['autoloadPath'] ); + } +} +?> diff --git a/library/ezc/Graph/src/axis/container.php b/library/ezc/Graph/src/axis/container.php index 7bebcfafaf..827250cba7 100644 --- a/library/ezc/Graph/src/axis/container.php +++ b/library/ezc/Graph/src/axis/container.php @@ -1,221 +1,221 @@ -chart = $chart; - } - - /** - * Returns if the given offset exists. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key Identifier of dataset. - * @return bool True when the offset exists, otherwise false. - */ - public function offsetExists( $key ) - { - return isset( $this->data[$key] ); - } - - /** - * Returns the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key Identifier of dataset. - * @return ezcGraphChartElementAxis - * - * @throws ezcBasePropertyNotFoundException - * If no dataset with identifier exists - */ - public function offsetGet( $key ) - { - if ( !isset( $this->data[$key] ) ) - { - throw new ezcBasePropertyNotFoundException( $key ); - } - - return $this->data[$key]; - } - - /** - * Set the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key - * @param ezcGraphChartElementAxis $value - * @return void - * - * @throws ezcBaseValueException - * If supplied value is not an ezcGraphChartElementAxis - */ - public function offsetSet( $key, $value ) - { - if ( !$value instanceof ezcGraphChartElementAxis ) - { - throw new ezcBaseValueException( $key, $value, 'ezcGraphChartElementAxis' ); - } - - if ( $key === null ) - { - $key = count( $this->data ); - } - - // Add axis and configure it with current font and palette - $this->data[$key] = $value; - $value->font = $this->chart->options->font; - $value->setFromPalette( $this->chart->palette ); - - return $value; - } - - /** - * Unset the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key - * @return void - */ - public function offsetUnset( $key ) - { - if ( !isset( $this->data[$key] ) ) - { - throw new ezcBasePropertyNotFoundException( $key ); - } - - unset( $this->data[$key] ); - } - - /** - * Returns the currently selected dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return ezcGraphChartElementAxis The currently selected dataset. - */ - public function current() - { - return current( $this->data ); - } - - /** - * Returns the next dataset and selects it or false on the last dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return mixed ezcGraphChartElementAxis if the next dataset exists, or false. - */ - public function next() - { - return next( $this->data ); - } - - /** - * Returns the key of the currently selected dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return int The key of the currently selected dataset. - */ - public function key() - { - return key( $this->data ); - } - - /** - * Returns if the current dataset is valid. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return bool If the current dataset is valid - */ - public function valid() - { - return ( current( $this->data ) !== false ); - } - - /** - * Selects the very first dataset and returns it. - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return ezcGraphChartElementAxis The very first dataset. - */ - public function rewind() - { - return reset( $this->data ); - } - - /** - * Returns the number of datasets in the row. - * - * This method is part of the Countable interface to allow the usage of - * PHP's count() function to check how many datasets exist. - * - * @return int Number of datasets. - */ - public function count() - { - return count( $this->data ); - } -} - -?> +chart = $chart; + } + + /** + * Returns if the given offset exists. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key Identifier of dataset. + * @return bool True when the offset exists, otherwise false. + */ + public function offsetExists( $key ) + { + return isset( $this->data[$key] ); + } + + /** + * Returns the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key Identifier of dataset. + * @return ezcGraphChartElementAxis + * + * @throws ezcBasePropertyNotFoundException + * If no dataset with identifier exists + */ + public function offsetGet( $key ) + { + if ( !isset( $this->data[$key] ) ) + { + throw new ezcBasePropertyNotFoundException( $key ); + } + + return $this->data[$key]; + } + + /** + * Set the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key + * @param ezcGraphChartElementAxis $value + * @return void + * + * @throws ezcBaseValueException + * If supplied value is not an ezcGraphChartElementAxis + */ + public function offsetSet( $key, $value ) + { + if ( !$value instanceof ezcGraphChartElementAxis ) + { + throw new ezcBaseValueException( $key, $value, 'ezcGraphChartElementAxis' ); + } + + if ( $key === null ) + { + $key = count( $this->data ); + } + + // Add axis and configure it with current font and palette + $this->data[$key] = $value; + $value->font = $this->chart->options->font; + $value->setFromPalette( $this->chart->palette ); + + return $value; + } + + /** + * Unset the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key + * @return void + */ + public function offsetUnset( $key ) + { + if ( !isset( $this->data[$key] ) ) + { + throw new ezcBasePropertyNotFoundException( $key ); + } + + unset( $this->data[$key] ); + } + + /** + * Returns the currently selected dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return ezcGraphChartElementAxis The currently selected dataset. + */ + public function current() + { + return current( $this->data ); + } + + /** + * Returns the next dataset and selects it or false on the last dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return mixed ezcGraphChartElementAxis if the next dataset exists, or false. + */ + public function next() + { + return next( $this->data ); + } + + /** + * Returns the key of the currently selected dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return int The key of the currently selected dataset. + */ + public function key() + { + return key( $this->data ); + } + + /** + * Returns if the current dataset is valid. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return bool If the current dataset is valid + */ + public function valid() + { + return ( current( $this->data ) !== false ); + } + + /** + * Selects the very first dataset and returns it. + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return ezcGraphChartElementAxis The very first dataset. + */ + public function rewind() + { + return reset( $this->data ); + } + + /** + * Returns the number of datasets in the row. + * + * This method is part of the Countable interface to allow the usage of + * PHP's count() function to check how many datasets exist. + * + * @return int Number of datasets. + */ + public function count() + { + return count( $this->data ); + } +} + +?> diff --git a/library/ezc/Graph/src/axis/date.php b/library/ezc/Graph/src/axis/date.php index 2a11ecfda4..94bb6a057c 100644 --- a/library/ezc/Graph/src/axis/date.php +++ b/library/ezc/Graph/src/axis/date.php @@ -1,633 +1,633 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->options->fillLines = 210; - * $graph->title = 'Concurrent requests'; - * $graph->legend = false; - * - * $graph->xAxis = new ezcGraphChartElementDateAxis(); - * - * // Add data - * $graph->data['Machine 1'] = new ezcGraphArrayDataSet( array( - * '8:00' => 3241, - * '8:13' => 934, - * '8:24' => 1201, - * '8:27' => 1752, - * '8:51' => 123, - * ) ); - * $graph->data['Machine 2'] = new ezcGraphArrayDataSet( array( - * '8:05' => 623, - * '8:12' => 2103, - * '8:33' => 543, - * '8:43' => 2034, - * '8:59' => 3410, - * ) ); - * - * $graph->data['Machine 1']->symbol = ezcGraph::BULLET; - * $graph->data['Machine 2']->symbol = ezcGraph::BULLET; - * - * $graph->render( 400, 150, 'tutorial_axis_datetime.svg' ); - * - * - * @property float $startDate - * Starting date used to display on axis. - * @property float $endDate - * End date used to display on axis. - * @property float $interval - * Time interval between steps on axis. - * @property string $dateFormat - * Format of date string - * Like http://php.net/date - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis -{ - - const MONTH = 2629800; - - const YEAR = 31536000; - - const DECADE = 315360000; - - /** - * Minimum inserted date - * - * @var int - */ - protected $minValue = false; - - /** - * Maximum inserted date - * - * @var int - */ - protected $maxValue = false; - - /** - * Nice time intervals to used if there is no user defined interval - * - * @var array - */ - protected $predefinedIntervals = array( - // Second - 1 => 'H:i.s', - // Ten seconds - 10 => 'H:i.s', - // Thirty seconds - 30 => 'H:i.s', - // Minute - 60 => 'H:i', - // Ten minutes - 600 => 'H:i', - // Half an hour - 1800 => 'H:i', - // Hour - 3600 => 'H:i', - // Four hours - 14400 => 'H:i', - // Six hours - 21600 => 'H:i', - // Half a day - 43200 => 'd.m a', - // Day - 86400 => 'd.m', - // Week - 604800 => 'W', - // Month - self::MONTH => 'M y', - // Year - self::YEAR => 'Y', - // Decade - self::DECADE => 'Y', - ); - - /** - * Constant used for calculation of automatic definition of major scaling - * steps - */ - const MAJOR_COUNT = 10; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['startDate'] = false; - $this->properties['endDate'] = false; - $this->properties['interval'] = false; - $this->properties['dateFormat'] = false; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'startDate': - $this->properties['startDate'] = (int) $propertyValue; - break; - case 'endDate': - $this->properties['endDate'] = (int) $propertyValue; - break; - case 'interval': - $this->properties['interval'] = (int) $propertyValue; - $this->properties['initialized'] = true; - break; - case 'dateFormat': - $this->properties['dateFormat'] = (string) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Ensure proper timestamp - * - * Takes a mixed value from datasets, like timestamps, or strings - * describing some time and converts it to a timestamp. - * - * @param mixed $value - * @return int - */ - protected static function ensureTimestamp( $value ) - { - if ( is_numeric( $value ) ) - { - $timestamp = (int) $value; - } - elseif ( ( $timestamp = strtotime( $value ) ) === false ) - { - throw new ezcGraphErrorParsingDateException( $value ); - } - - return $timestamp; - } - - /** - * Add data for this axis - * - * @param array $values Value which will be displayed on this axis - * @return void - */ - public function addData( array $values ) - { - foreach ( $values as $nr => $value ) - { - $value = self::ensureTimestamp( $value ); - - if ( $this->minValue === false || - $value < $this->minValue ) - { - $this->minValue = $value; - } - - if ( $this->maxValue === false || - $value > $this->maxValue ) - { - $this->maxValue = $value; - } - } - - $this->properties['initialized'] = true; - } - - /** - * Calculate nice time interval - * - * Use the best fitting time interval defined in class property array - * predefinedIntervals. - * - * @param int $min Start time - * @param int $max End time - * @return void - */ - protected function calculateInterval( $min, $max ) - { - $diff = $max - $min; - - foreach ( $this->predefinedIntervals as $interval => $format ) - { - if ( ( $diff / $interval ) <= self::MAJOR_COUNT ) - { - break; - } - } - - if ( ( $this->properties['startDate'] !== false ) && - ( $this->properties['endDate'] !== false ) ) - { - // Use interval between defined borders - if ( ( $diff % $interval ) > 0 ) - { - // Stil use predefined date format from old interval if not set - if ( $this->properties['dateFormat'] === false ) - { - $this->properties['dateFormat'] = $this->predefinedIntervals[$interval]; - } - - $count = ceil( $diff / $interval ); - $interval = round( $diff / $count, 0 ); - } - } - - $this->properties['interval'] = $interval; - } - - /** - * Calculate lower nice date - * - * Calculates a date which is earlier or equal to the given date, and is - * divisible by the given interval. - * - * @param int $min Date - * @param int $interval Interval - * @return int Earlier date - */ - protected function calculateLowerNiceDate( $min, $interval ) - { - switch ( $interval ) - { - case self::MONTH: - // Special handling for months - not covered by the default - // algorithm - return mktime( - 1, - 0, - 0, - (int) date( 'm', $min ), - 1, - (int) date( 'Y', $min ) - ); - default: - $dateSteps = array( 60, 60, 24, 7, 52 ); - - $date = array( - (int) date( 's', $min ), - (int) date( 'i', $min ), - (int) date( 'H', $min ), - (int) date( 'd', $min ), - (int) date( 'm', $min ), - (int) date( 'Y', $min ), - ); - - $element = 0; - while ( ( $step = array_shift( $dateSteps ) ) && - ( $interval > $step ) ) - { - $interval /= $step; - $date[$element++] = (int) ( $element > 2 ); - } - - $date[$element] -= $date[$element] % $interval; - - return mktime( - $date[2], - $date[1], - $date[0], - $date[4], - $date[3], - $date[5] - ); - } - } - - /** - * Calculate start date - * - * Use calculateLowerNiceDate to get a date earlier or equal date then the - * minimum date to use it as the start date for the axis depending on the - * selected interval. - * - * @param mixed $min Minimum date - * @param mixed $max Maximum date - * @return void - */ - public function calculateMinimum( $min, $max ) - { - if ( $this->properties['endDate'] === false ) - { - $this->properties['startDate'] = $this->calculateLowerNiceDate( $min, $this->interval ); - } - else - { - $this->properties['startDate'] = $this->properties['endDate']; - - while ( $this->properties['startDate'] > $min ) - { - switch ( $this->interval ) - { - case self::MONTH: - $this->properties['startDate'] = strtotime( '-1 month', $this->properties['startDate'] ); - break; - case self::YEAR: - $this->properties['startDate'] = strtotime( '-1 year', $this->properties['startDate'] ); - break; - case self::DECADE: - $this->properties['startDate'] = strtotime( '-10 years', $this->properties['startDate'] ); - break; - default: - $this->properties['startDate'] -= $this->interval; - } - } - } - } - - /** - * Calculate end date - * - * Use calculateLowerNiceDate to get a date later or equal date then the - * maximum date to use it as the end date for the axis depending on the - * selected interval. - * - * @param mixed $min Minimum date - * @param mixed $max Maximum date - * @return void - */ - public function calculateMaximum( $min, $max ) - { - $this->properties['endDate'] = $this->properties['startDate']; - - while ( $this->properties['endDate'] < $max ) - { - switch ( $this->interval ) - { - case self::MONTH: - $this->properties['endDate'] = strtotime( '+1 month', $this->properties['endDate'] ); - break; - case self::YEAR: - $this->properties['endDate'] = strtotime( '+1 year', $this->properties['endDate'] ); - break; - case self::DECADE: - $this->properties['endDate'] = strtotime( '+10 years', $this->properties['endDate'] ); - break; - default: - $this->properties['endDate'] += $this->interval; - } - } - } - - /** - * Calculate axis bounding values on base of the assigned values - * - * @return void - */ - public function calculateAxisBoundings() - { - // Prevent division by zero, when min == max - if ( $this->minValue == $this->maxValue ) - { - if ( $this->minValue == 0 ) - { - $this->maxValue = 1; - } - else - { - $this->minValue -= ( $this->minValue * .1 ); - $this->maxValue += ( $this->maxValue * .1 ); - } - } - - // Use custom minimum and maximum if available - if ( $this->properties['startDate'] !== false ) - { - $this->minValue = $this->properties['startDate']; - } - - if ( $this->properties['endDate'] !== false ) - { - $this->maxValue = $this->properties['endDate']; - } - - // Calculate "nice" values for scaling parameters - if ( $this->properties['interval'] === false ) - { - $this->calculateInterval( $this->minValue, $this->maxValue ); - } - - if ( $this->properties['dateFormat'] === false && isset( $this->predefinedIntervals[$this->interval] ) ) - { - $this->properties['dateFormat'] = $this->predefinedIntervals[$this->interval]; - } - - if ( $this->properties['startDate'] === false ) - { - $this->calculateMinimum( $this->minValue, $this->maxValue ); - } - - if ( $this->properties['endDate'] === false ) - { - $this->calculateMaximum( $this->minValue, $this->maxValue ); - } - } - - /** - * Get coordinate for a dedicated value on the chart - * - * @param float $value Value to determine position for - * @return float Position on chart - */ - public function getCoordinate( $value ) - { - // Force typecast, because ( false < -100 ) results in (bool) true - $intValue = ( $value === false ? false : self::ensureTimestamp( $value ) ); - - if ( ( $value === false ) && - ( ( $intValue < $this->startDate ) || ( $intValue > $this->endDate ) ) ) - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return 0.; - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1.; - } - } - else - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1 - ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); - } - } - } - - /** - * Return count of minor steps - * - * @return integer Count of minor steps - */ - public function getMinorStepCount() - { - return false; - } - - /** - * Return count of major steps - * - * @return integer Count of major steps - */ - public function getMajorStepCount() - { - return (int) ceil( ( $this->properties['endDate'] - $this->startDate ) / $this->interval ); - } - - /** - * Get label for a dedicated step on the axis - * - * @param integer $step Number of step - * @return string label - */ - public function getLabel( $step ) - { - return $this->getLabelFromTimestamp( $this->startDate + ( $step * $this->interval ), $step ); - } - - /** - * Get label for timestamp - * - * @param int $time - * @param int $step - * @return string - */ - protected function getLabelFromTimestamp( $time, $step ) - { - if ( $this->properties['labelCallback'] !== null ) - { - return call_user_func_array( - $this->properties['labelCallback'], - array( - date( $this->properties['dateFormat'], $time ), - $step, - ) - ); - } - else - { - return date( $this->properties['dateFormat'], $time ); - } - } - - /** - * Return array of steps on this axis - * - * @return array( ezcGraphAxisStep ) - */ - public function getSteps() - { - $steps = array(); - - $start = $this->properties['startDate']; - $end = $this->properties['endDate']; - $distance = $end - $start; - - $step = 0; - for ( $time = $start; $time <= $end; ) - { - $steps[] = new ezcGraphAxisStep( - ( $time - $start ) / $distance, - $this->interval / $distance, - $this->getLabelFromTimestamp( $time, $step++ ), - array(), - $step === 1, - $time >= $end - ); - - switch ( $this->interval ) - { - case self::MONTH: - $time = strtotime( '+1 month', $time ); - break; - case self::YEAR: - $time = strtotime( '+1 year', $time ); - break; - case self::DECADE: - $time = strtotime( '+10 years', $time ); - break; - default: - $time += $this->interval; - break; - } - } - - return $steps; - } - - /** - * Is zero step - * - * Returns true if the given step is the one on the initial axis position - * - * @param int $step Number of step - * @return bool Status If given step is initial axis position - */ - public function isZeroStep( $step ) - { - return ( $step == 0 ); - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->options->fillLines = 210; + * $graph->title = 'Concurrent requests'; + * $graph->legend = false; + * + * $graph->xAxis = new ezcGraphChartElementDateAxis(); + * + * // Add data + * $graph->data['Machine 1'] = new ezcGraphArrayDataSet( array( + * '8:00' => 3241, + * '8:13' => 934, + * '8:24' => 1201, + * '8:27' => 1752, + * '8:51' => 123, + * ) ); + * $graph->data['Machine 2'] = new ezcGraphArrayDataSet( array( + * '8:05' => 623, + * '8:12' => 2103, + * '8:33' => 543, + * '8:43' => 2034, + * '8:59' => 3410, + * ) ); + * + * $graph->data['Machine 1']->symbol = ezcGraph::BULLET; + * $graph->data['Machine 2']->symbol = ezcGraph::BULLET; + * + * $graph->render( 400, 150, 'tutorial_axis_datetime.svg' ); + * + * + * @property float $startDate + * Starting date used to display on axis. + * @property float $endDate + * End date used to display on axis. + * @property float $interval + * Time interval between steps on axis. + * @property string $dateFormat + * Format of date string + * Like http://php.net/date + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis +{ + + const MONTH = 2629800; + + const YEAR = 31536000; + + const DECADE = 315360000; + + /** + * Minimum inserted date + * + * @var int + */ + protected $minValue = false; + + /** + * Maximum inserted date + * + * @var int + */ + protected $maxValue = false; + + /** + * Nice time intervals to used if there is no user defined interval + * + * @var array + */ + protected $predefinedIntervals = array( + // Second + 1 => 'H:i.s', + // Ten seconds + 10 => 'H:i.s', + // Thirty seconds + 30 => 'H:i.s', + // Minute + 60 => 'H:i', + // Ten minutes + 600 => 'H:i', + // Half an hour + 1800 => 'H:i', + // Hour + 3600 => 'H:i', + // Four hours + 14400 => 'H:i', + // Six hours + 21600 => 'H:i', + // Half a day + 43200 => 'd.m a', + // Day + 86400 => 'd.m', + // Week + 604800 => 'W', + // Month + self::MONTH => 'M y', + // Year + self::YEAR => 'Y', + // Decade + self::DECADE => 'Y', + ); + + /** + * Constant used for calculation of automatic definition of major scaling + * steps + */ + const MAJOR_COUNT = 10; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['startDate'] = false; + $this->properties['endDate'] = false; + $this->properties['interval'] = false; + $this->properties['dateFormat'] = false; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'startDate': + $this->properties['startDate'] = (int) $propertyValue; + break; + case 'endDate': + $this->properties['endDate'] = (int) $propertyValue; + break; + case 'interval': + $this->properties['interval'] = (int) $propertyValue; + $this->properties['initialized'] = true; + break; + case 'dateFormat': + $this->properties['dateFormat'] = (string) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Ensure proper timestamp + * + * Takes a mixed value from datasets, like timestamps, or strings + * describing some time and converts it to a timestamp. + * + * @param mixed $value + * @return int + */ + protected static function ensureTimestamp( $value ) + { + if ( is_numeric( $value ) ) + { + $timestamp = (int) $value; + } + elseif ( ( $timestamp = strtotime( $value ) ) === false ) + { + throw new ezcGraphErrorParsingDateException( $value ); + } + + return $timestamp; + } + + /** + * Add data for this axis + * + * @param array $values Value which will be displayed on this axis + * @return void + */ + public function addData( array $values ) + { + foreach ( $values as $nr => $value ) + { + $value = self::ensureTimestamp( $value ); + + if ( $this->minValue === false || + $value < $this->minValue ) + { + $this->minValue = $value; + } + + if ( $this->maxValue === false || + $value > $this->maxValue ) + { + $this->maxValue = $value; + } + } + + $this->properties['initialized'] = true; + } + + /** + * Calculate nice time interval + * + * Use the best fitting time interval defined in class property array + * predefinedIntervals. + * + * @param int $min Start time + * @param int $max End time + * @return void + */ + protected function calculateInterval( $min, $max ) + { + $diff = $max - $min; + + foreach ( $this->predefinedIntervals as $interval => $format ) + { + if ( ( $diff / $interval ) <= self::MAJOR_COUNT ) + { + break; + } + } + + if ( ( $this->properties['startDate'] !== false ) && + ( $this->properties['endDate'] !== false ) ) + { + // Use interval between defined borders + if ( ( $diff % $interval ) > 0 ) + { + // Stil use predefined date format from old interval if not set + if ( $this->properties['dateFormat'] === false ) + { + $this->properties['dateFormat'] = $this->predefinedIntervals[$interval]; + } + + $count = ceil( $diff / $interval ); + $interval = round( $diff / $count, 0 ); + } + } + + $this->properties['interval'] = $interval; + } + + /** + * Calculate lower nice date + * + * Calculates a date which is earlier or equal to the given date, and is + * divisible by the given interval. + * + * @param int $min Date + * @param int $interval Interval + * @return int Earlier date + */ + protected function calculateLowerNiceDate( $min, $interval ) + { + switch ( $interval ) + { + case self::MONTH: + // Special handling for months - not covered by the default + // algorithm + return mktime( + 1, + 0, + 0, + (int) date( 'm', $min ), + 1, + (int) date( 'Y', $min ) + ); + default: + $dateSteps = array( 60, 60, 24, 7, 52 ); + + $date = array( + (int) date( 's', $min ), + (int) date( 'i', $min ), + (int) date( 'H', $min ), + (int) date( 'd', $min ), + (int) date( 'm', $min ), + (int) date( 'Y', $min ), + ); + + $element = 0; + while ( ( $step = array_shift( $dateSteps ) ) && + ( $interval > $step ) ) + { + $interval /= $step; + $date[$element++] = (int) ( $element > 2 ); + } + + $date[$element] -= $date[$element] % $interval; + + return mktime( + $date[2], + $date[1], + $date[0], + $date[4], + $date[3], + $date[5] + ); + } + } + + /** + * Calculate start date + * + * Use calculateLowerNiceDate to get a date earlier or equal date then the + * minimum date to use it as the start date for the axis depending on the + * selected interval. + * + * @param mixed $min Minimum date + * @param mixed $max Maximum date + * @return void + */ + public function calculateMinimum( $min, $max ) + { + if ( $this->properties['endDate'] === false ) + { + $this->properties['startDate'] = $this->calculateLowerNiceDate( $min, $this->interval ); + } + else + { + $this->properties['startDate'] = $this->properties['endDate']; + + while ( $this->properties['startDate'] > $min ) + { + switch ( $this->interval ) + { + case self::MONTH: + $this->properties['startDate'] = strtotime( '-1 month', $this->properties['startDate'] ); + break; + case self::YEAR: + $this->properties['startDate'] = strtotime( '-1 year', $this->properties['startDate'] ); + break; + case self::DECADE: + $this->properties['startDate'] = strtotime( '-10 years', $this->properties['startDate'] ); + break; + default: + $this->properties['startDate'] -= $this->interval; + } + } + } + } + + /** + * Calculate end date + * + * Use calculateLowerNiceDate to get a date later or equal date then the + * maximum date to use it as the end date for the axis depending on the + * selected interval. + * + * @param mixed $min Minimum date + * @param mixed $max Maximum date + * @return void + */ + public function calculateMaximum( $min, $max ) + { + $this->properties['endDate'] = $this->properties['startDate']; + + while ( $this->properties['endDate'] < $max ) + { + switch ( $this->interval ) + { + case self::MONTH: + $this->properties['endDate'] = strtotime( '+1 month', $this->properties['endDate'] ); + break; + case self::YEAR: + $this->properties['endDate'] = strtotime( '+1 year', $this->properties['endDate'] ); + break; + case self::DECADE: + $this->properties['endDate'] = strtotime( '+10 years', $this->properties['endDate'] ); + break; + default: + $this->properties['endDate'] += $this->interval; + } + } + } + + /** + * Calculate axis bounding values on base of the assigned values + * + * @return void + */ + public function calculateAxisBoundings() + { + // Prevent division by zero, when min == max + if ( $this->minValue == $this->maxValue ) + { + if ( $this->minValue == 0 ) + { + $this->maxValue = 1; + } + else + { + $this->minValue -= ( $this->minValue * .1 ); + $this->maxValue += ( $this->maxValue * .1 ); + } + } + + // Use custom minimum and maximum if available + if ( $this->properties['startDate'] !== false ) + { + $this->minValue = $this->properties['startDate']; + } + + if ( $this->properties['endDate'] !== false ) + { + $this->maxValue = $this->properties['endDate']; + } + + // Calculate "nice" values for scaling parameters + if ( $this->properties['interval'] === false ) + { + $this->calculateInterval( $this->minValue, $this->maxValue ); + } + + if ( $this->properties['dateFormat'] === false && isset( $this->predefinedIntervals[$this->interval] ) ) + { + $this->properties['dateFormat'] = $this->predefinedIntervals[$this->interval]; + } + + if ( $this->properties['startDate'] === false ) + { + $this->calculateMinimum( $this->minValue, $this->maxValue ); + } + + if ( $this->properties['endDate'] === false ) + { + $this->calculateMaximum( $this->minValue, $this->maxValue ); + } + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param float $value Value to determine position for + * @return float Position on chart + */ + public function getCoordinate( $value ) + { + // Force typecast, because ( false < -100 ) results in (bool) true + $intValue = ( $value === false ? false : self::ensureTimestamp( $value ) ); + + if ( ( $value === false ) && + ( ( $intValue < $this->startDate ) || ( $intValue > $this->endDate ) ) ) + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return 0.; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1.; + } + } + else + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1 - ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate ); + } + } + } + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + public function getMinorStepCount() + { + return false; + } + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + public function getMajorStepCount() + { + return (int) ceil( ( $this->properties['endDate'] - $this->startDate ) / $this->interval ); + } + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + public function getLabel( $step ) + { + return $this->getLabelFromTimestamp( $this->startDate + ( $step * $this->interval ), $step ); + } + + /** + * Get label for timestamp + * + * @param int $time + * @param int $step + * @return string + */ + protected function getLabelFromTimestamp( $time, $step ) + { + if ( $this->properties['labelCallback'] !== null ) + { + return call_user_func_array( + $this->properties['labelCallback'], + array( + date( $this->properties['dateFormat'], $time ), + $step, + ) + ); + } + else + { + return date( $this->properties['dateFormat'], $time ); + } + } + + /** + * Return array of steps on this axis + * + * @return array( ezcGraphAxisStep ) + */ + public function getSteps() + { + $steps = array(); + + $start = $this->properties['startDate']; + $end = $this->properties['endDate']; + $distance = $end - $start; + + $step = 0; + for ( $time = $start; $time <= $end; ) + { + $steps[] = new ezcGraphAxisStep( + ( $time - $start ) / $distance, + $this->interval / $distance, + $this->getLabelFromTimestamp( $time, $step++ ), + array(), + $step === 1, + $time >= $end + ); + + switch ( $this->interval ) + { + case self::MONTH: + $time = strtotime( '+1 month', $time ); + break; + case self::YEAR: + $time = strtotime( '+1 year', $time ); + break; + case self::DECADE: + $time = strtotime( '+10 years', $time ); + break; + default: + $time += $this->interval; + break; + } + } + + return $steps; + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + public function isZeroStep( $step ) + { + return ( $step == 0 ); + } +} + +?> diff --git a/library/ezc/Graph/src/axis/labeled.php b/library/ezc/Graph/src/axis/labeled.php index 3d4a1bff73..23f1c646ba 100644 --- a/library/ezc/Graph/src/axis/labeled.php +++ b/library/ezc/Graph/src/axis/labeled.php @@ -1,520 +1,520 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->options->fillLines = 210; - * $graph->options->font->maxFontSize = 10; - * $graph->title = 'Error level colors'; - * $graph->legend = false; - * - * $graph->yAxis = new ezcGraphChartElementLabeledAxis(); - * $graph->yAxis->axisLabelRenderer->showZeroValue = true; - * - * $graph->yAxis->label = 'Color'; - * $graph->xAxis->label = 'Error level'; - * - * // Add data - * $graph->data['colors'] = new ezcGraphArrayDataSet( - * array( - * 'info' => 'blue', - * 'notice' => 'green', - * 'warning' => 'orange', - * 'error' => 'red', - * 'fatal' => 'red', - * ) - * ); - * - * $graph->render( 400, 150, 'tutorial_axis_labeled.svg' ); - * - * - * @property float $labelCount - * Define count of displayed labels on the axis - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementLabeledAxis extends ezcGraphChartElementAxis -{ - /** - * Array with labeles for data - * - * @var array - */ - protected $labels = array(); - - /** - * Labels indexed by their name as key for faster lookups - * - * @var array - */ - protected $labelsIndexed = array(); - - /** - * Reduced amount of labels which will be displayed in the chart - * - * @var array - */ - protected $displayedLabels = array(); - - /** - * Maximum count of labels which can be displayed on one axis - * @todo Perhaps base this on the chart size - */ - const MAX_LABEL_COUNT = 10; - - /** - * Precalculated steps on the axis - * - * @var array(ezcGraphAxisStep) - */ - protected $steps; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['labelCount'] = null; - - $this->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'labelCount': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); - } - - $this->properties['labelCount'] = (int) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Increase the keys of all elements in the array up from the start key, to - * insert an additional element at the correct position. - * - * @param array $array Array - * @param int $startKey Key to increase keys from - * @return array Updated array - */ - protected function increaseKeys( array $array, $startKey ) - { - foreach ( $array as $key => $value ) - { - if ( $key === $startKey ) - { - // Recursive check, if next key should be increased, too - if ( isset ( $array[$key + 1] ) ) - { - $array = $this->increaseKeys( $array, $key + 1 ); - } - - // Increase key - $array[$key + 1] = $array[$key]; - unset( $array[$key] ); - } - } - - return $array; - } - - /** - * Provide initial set of labels - * - * This method may be used to provide an ordered set of labels, containing - * labels, which are not available in the datasets or to provide a label - * order different to the one in the given dataset. - * - * @param array $labels - * @return void - */ - public function provideLabels( array $labels ) - { - $this->addData( $labels ); - } - - /** - * Add data for this axis - * - * @param array $values Value which will be displayed on this axis - * @return void - */ - public function addData( array $values ) - { - $position = 0; - foreach ( $values as $label ) - { - $label = (string) $label; - - if ( !in_array( $label, $this->labels, true ) ) - { - if ( isset( $this->labels[$position] ) ) - { - $this->labels = $this->increaseKeys( $this->labels, $position ); - $this->labels[$position++] = $label; - } - else - { - $this->labels[$position++] = $label; - } - } - else - { - $position = array_search( $label, $this->labels, true ) + 1; - } - } - ksort( $this->labels ); - $this->labelsIndexed = array_flip( $this->labels ); - - $this->properties['initialized'] = true; - } - - /** - * Calculate axis bounding values on base of the assigned values - * - * @abstract - * @access public - * @return void - */ - public function calculateAxisBoundings() - { - $this->steps = array(); - - // Apply label format callback function - if ( $this->properties['labelCallback'] !== null ) - { - foreach ( $this->labels as $nr => $label ) - { - $this->labels[$nr] = call_user_func_array( - $this->properties['labelCallback'], - array( - $label, - $nr - ) - ); - } - } - - $labelCount = count( $this->labels ) - 1; - - if ( $labelCount === 0 ) - { - // Create single only step - $this->steps = array( - new ezcGraphAxisStep( - 0, - 1, - reset( $this->labels ), - array(), - true, - true - ), - ); - - return true; - } - - if ( $this->properties['labelCount'] === null ) - { - if ( $labelCount <= self::MAX_LABEL_COUNT ) - { - $stepSize = 1 / $labelCount; - - foreach ( $this->labels as $nr => $label ) - { - $this->steps[] = new ezcGraphAxisStep( - $stepSize * $nr, - $stepSize, - $label, - array(), - $nr === 0, - $nr === $labelCount - ); - } - - // @TODO: This line is deprecated and only build for - // deprecated getLabel() - $this->displayedLabels = $this->labels; - - return true; - } - - for ( $div = self::MAX_LABEL_COUNT; $div > 1; --$div ) - { - if ( ( $labelCount % $div ) === 0 ) - { - // @TODO: This part is deprecated and only build for - // deprecated getLabel() - $step = $labelCount / $div; - - foreach ( $this->labels as $nr => $label ) - { - if ( ( $nr % $step ) === 0 ) - { - $this->displayedLabels[] = $label; - } - } - // End of deprecated part - - break; - } - } - } - else - { - $div = false; - } - - // Build up step array - if ( $div > 2 ) - { - $step = $labelCount / $div; - $stepSize = 1 / $div; - $minorStepSize = $stepSize / $step; - - foreach ( $this->labels as $nr => $label ) - { - if ( ( $nr % $step ) === 0 ) - { - $mainstep = new ezcGraphAxisStep( - $stepSize * ( $nr / $step ), - $stepSize, - $label, - array(), - $nr === 0, - $nr === $labelCount - ); - - $this->steps[] = $mainstep; - } - else - { - $mainstep->childs[] = new ezcGraphAxisStep( - $mainstep->position + $minorStepSize * ( $nr % $step ), - $minorStepSize - ); - } - } - } - else - { - if ( $this->properties['labelCount'] === null ) - { - $floatStep = $labelCount / ( self::MAX_LABEL_COUNT - 1 ); - } - else - { - $floatStep = $labelCount / min( $labelCount, $this->properties['labelCount'] - 1 ); - } - - $position = 0; - $minorStepSize = 1 / $labelCount; - - foreach ( $this->labels as $nr => $label ) - { - if ( $nr >= $position ) - { - $position += $floatStep; - - // Add as major step - $mainstep = new ezcGraphAxisStep( - $minorStepSize * $nr, - ceil( $position - $nr ) * $minorStepSize, - $label, - array(), - $nr === 0, - $nr === $labelCount - ); - - // @TODO: This line is deprecated and only build for - // deprecated getLabel() - $this->displayedLabels[] = $label; - - $this->steps[] = $mainstep; - } - else - { - $mainstep->childs[] = new ezcGraphAxisStep( - $minorStepSize * $nr, - $minorStepSize - ); - } - } - } - } - - /** - * Return array of steps on this axis - * - * @return array( ezcGraphAxisStep ) - */ - public function getSteps() - { - return $this->steps; - } - - /** - * Get coordinate for a dedicated value on the chart - * - * @param string $value Value to determine position for - * @return float Position on chart - */ - public function getCoordinate( $value ) - { - if ( ( $value === false ) || - ( $value === null ) || - ( !isset( $this->labelsIndexed[$value] ) ) ) - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return 0.; - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1.; - } - } - else - { - $key = $this->labelsIndexed[$value]; - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - if ( count( $this->labels ) > 1 ) - { - return (float) $key / ( count ( $this->labels ) - 1 ); - } - else - { - return 0; - } - case ezcGraph::BOTTOM: - case ezcGraph::RIGHT: - if ( count( $this->labels ) > 1 ) - { - return (float) 1 - $key / ( count ( $this->labels ) - 1 ); - } - else - { - return 1; - } - } - } - } - - /** - * Return count of minor steps - * - * @return integer Count of minor steps - */ - public function getMinorStepCount() - { - return 0; - } - - /** - * Return count of major steps - * - * @return integer Count of major steps - */ - public function getMajorStepCount() - { - return max( count( $this->displayedLabels ) - 1, 1 ); - } - - /** - * Get label for a dedicated step on the axis - * - * @param integer $step Number of step - * @return string label - */ - public function getLabel( $step ) - { - if ( isset( $this->displayedLabels[$step] ) ) - { - return $this->displayedLabels[$step]; - } - else - { - return false; - } - } - - /** - * Is zero step - * - * Returns true if the given step is the one on the initial axis position - * - * @param int $step Number of step - * @return bool Status If given step is initial axis position - */ - public function isZeroStep( $step ) - { - return !$step; - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->options->fillLines = 210; + * $graph->options->font->maxFontSize = 10; + * $graph->title = 'Error level colors'; + * $graph->legend = false; + * + * $graph->yAxis = new ezcGraphChartElementLabeledAxis(); + * $graph->yAxis->axisLabelRenderer->showZeroValue = true; + * + * $graph->yAxis->label = 'Color'; + * $graph->xAxis->label = 'Error level'; + * + * // Add data + * $graph->data['colors'] = new ezcGraphArrayDataSet( + * array( + * 'info' => 'blue', + * 'notice' => 'green', + * 'warning' => 'orange', + * 'error' => 'red', + * 'fatal' => 'red', + * ) + * ); + * + * $graph->render( 400, 150, 'tutorial_axis_labeled.svg' ); + * + * + * @property float $labelCount + * Define count of displayed labels on the axis + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementLabeledAxis extends ezcGraphChartElementAxis +{ + /** + * Array with labeles for data + * + * @var array + */ + protected $labels = array(); + + /** + * Labels indexed by their name as key for faster lookups + * + * @var array + */ + protected $labelsIndexed = array(); + + /** + * Reduced amount of labels which will be displayed in the chart + * + * @var array + */ + protected $displayedLabels = array(); + + /** + * Maximum count of labels which can be displayed on one axis + * @todo Perhaps base this on the chart size + */ + const MAX_LABEL_COUNT = 10; + + /** + * Precalculated steps on the axis + * + * @var array(ezcGraphAxisStep) + */ + protected $steps; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['labelCount'] = null; + + $this->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'labelCount': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); + } + + $this->properties['labelCount'] = (int) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Increase the keys of all elements in the array up from the start key, to + * insert an additional element at the correct position. + * + * @param array $array Array + * @param int $startKey Key to increase keys from + * @return array Updated array + */ + protected function increaseKeys( array $array, $startKey ) + { + foreach ( $array as $key => $value ) + { + if ( $key === $startKey ) + { + // Recursive check, if next key should be increased, too + if ( isset ( $array[$key + 1] ) ) + { + $array = $this->increaseKeys( $array, $key + 1 ); + } + + // Increase key + $array[$key + 1] = $array[$key]; + unset( $array[$key] ); + } + } + + return $array; + } + + /** + * Provide initial set of labels + * + * This method may be used to provide an ordered set of labels, containing + * labels, which are not available in the datasets or to provide a label + * order different to the one in the given dataset. + * + * @param array $labels + * @return void + */ + public function provideLabels( array $labels ) + { + $this->addData( $labels ); + } + + /** + * Add data for this axis + * + * @param array $values Value which will be displayed on this axis + * @return void + */ + public function addData( array $values ) + { + $position = 0; + foreach ( $values as $label ) + { + $label = (string) $label; + + if ( !in_array( $label, $this->labels, true ) ) + { + if ( isset( $this->labels[$position] ) ) + { + $this->labels = $this->increaseKeys( $this->labels, $position ); + $this->labels[$position++] = $label; + } + else + { + $this->labels[$position++] = $label; + } + } + else + { + $position = array_search( $label, $this->labels, true ) + 1; + } + } + ksort( $this->labels ); + $this->labelsIndexed = array_flip( $this->labels ); + + $this->properties['initialized'] = true; + } + + /** + * Calculate axis bounding values on base of the assigned values + * + * @abstract + * @access public + * @return void + */ + public function calculateAxisBoundings() + { + $this->steps = array(); + + // Apply label format callback function + if ( $this->properties['labelCallback'] !== null ) + { + foreach ( $this->labels as $nr => $label ) + { + $this->labels[$nr] = call_user_func_array( + $this->properties['labelCallback'], + array( + $label, + $nr + ) + ); + } + } + + $labelCount = count( $this->labels ) - 1; + + if ( $labelCount === 0 ) + { + // Create single only step + $this->steps = array( + new ezcGraphAxisStep( + 0, + 1, + reset( $this->labels ), + array(), + true, + true + ), + ); + + return true; + } + + if ( $this->properties['labelCount'] === null ) + { + if ( $labelCount <= self::MAX_LABEL_COUNT ) + { + $stepSize = 1 / $labelCount; + + foreach ( $this->labels as $nr => $label ) + { + $this->steps[] = new ezcGraphAxisStep( + $stepSize * $nr, + $stepSize, + $label, + array(), + $nr === 0, + $nr === $labelCount + ); + } + + // @TODO: This line is deprecated and only build for + // deprecated getLabel() + $this->displayedLabels = $this->labels; + + return true; + } + + for ( $div = self::MAX_LABEL_COUNT; $div > 1; --$div ) + { + if ( ( $labelCount % $div ) === 0 ) + { + // @TODO: This part is deprecated and only build for + // deprecated getLabel() + $step = $labelCount / $div; + + foreach ( $this->labels as $nr => $label ) + { + if ( ( $nr % $step ) === 0 ) + { + $this->displayedLabels[] = $label; + } + } + // End of deprecated part + + break; + } + } + } + else + { + $div = false; + } + + // Build up step array + if ( $div > 2 ) + { + $step = $labelCount / $div; + $stepSize = 1 / $div; + $minorStepSize = $stepSize / $step; + + foreach ( $this->labels as $nr => $label ) + { + if ( ( $nr % $step ) === 0 ) + { + $mainstep = new ezcGraphAxisStep( + $stepSize * ( $nr / $step ), + $stepSize, + $label, + array(), + $nr === 0, + $nr === $labelCount + ); + + $this->steps[] = $mainstep; + } + else + { + $mainstep->childs[] = new ezcGraphAxisStep( + $mainstep->position + $minorStepSize * ( $nr % $step ), + $minorStepSize + ); + } + } + } + else + { + if ( $this->properties['labelCount'] === null ) + { + $floatStep = $labelCount / ( self::MAX_LABEL_COUNT - 1 ); + } + else + { + $floatStep = $labelCount / min( $labelCount, $this->properties['labelCount'] - 1 ); + } + + $position = 0; + $minorStepSize = 1 / $labelCount; + + foreach ( $this->labels as $nr => $label ) + { + if ( $nr >= $position ) + { + $position += $floatStep; + + // Add as major step + $mainstep = new ezcGraphAxisStep( + $minorStepSize * $nr, + ceil( $position - $nr ) * $minorStepSize, + $label, + array(), + $nr === 0, + $nr === $labelCount + ); + + // @TODO: This line is deprecated and only build for + // deprecated getLabel() + $this->displayedLabels[] = $label; + + $this->steps[] = $mainstep; + } + else + { + $mainstep->childs[] = new ezcGraphAxisStep( + $minorStepSize * $nr, + $minorStepSize + ); + } + } + } + } + + /** + * Return array of steps on this axis + * + * @return array( ezcGraphAxisStep ) + */ + public function getSteps() + { + return $this->steps; + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param string $value Value to determine position for + * @return float Position on chart + */ + public function getCoordinate( $value ) + { + if ( ( $value === false ) || + ( $value === null ) || + ( !isset( $this->labelsIndexed[$value] ) ) ) + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return 0.; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1.; + } + } + else + { + $key = $this->labelsIndexed[$value]; + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + if ( count( $this->labels ) > 1 ) + { + return (float) $key / ( count ( $this->labels ) - 1 ); + } + else + { + return 0; + } + case ezcGraph::BOTTOM: + case ezcGraph::RIGHT: + if ( count( $this->labels ) > 1 ) + { + return (float) 1 - $key / ( count ( $this->labels ) - 1 ); + } + else + { + return 1; + } + } + } + } + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + public function getMinorStepCount() + { + return 0; + } + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + public function getMajorStepCount() + { + return max( count( $this->displayedLabels ) - 1, 1 ); + } + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + public function getLabel( $step ) + { + if ( isset( $this->displayedLabels[$step] ) ) + { + return $this->displayedLabels[$step]; + } + else + { + return false; + } + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + public function isZeroStep( $step ) + { + return !$step; + } +} + +?> diff --git a/library/ezc/Graph/src/axis/logarithmic.php b/library/ezc/Graph/src/axis/logarithmic.php index 92292c5400..53c82efbe8 100644 --- a/library/ezc/Graph/src/axis/logarithmic.php +++ b/library/ezc/Graph/src/axis/logarithmic.php @@ -1,341 +1,341 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->title = 'The power of x'; - * $graph->legend->position = ezcGraph::BOTTOM; - * - * $graph->xAxis = new ezcGraphChartElementNumericAxis(); - * $graph->yAxis = new ezcGraphChartElementLogarithmicalAxis(); - * - * $graph->data['x^2'] = new ezcGraphNumericDataSet( - * -10, 10, - * create_function( '$x', 'return pow( $x, 2 ) + 1;' ) - * ); - * - * $graph->data['x^4'] = new ezcGraphNumericDataSet( - * -10, 10, - * create_function( '$x', 'return pow( $x, 4 ) + 1;' ) - * ); - * - * $graph->data['x^6'] = new ezcGraphNumericDataSet( - * -10, 10, - * create_function( '$x', 'return pow( $x, 6 ) + 1;' ) - * ); - * - * $graph->render( 400, 250, 'tutorial_axis_logarithmic.svg' ); - * - * - * @property float $base - * Base for logarithmical scaling. - * @property string $logarithmicalFormatString - * Sprintf formatstring for the axis labels where - * $1 is the base and - * $2 is the exponent. - * @property-read float $minValue - * Minimum Value to display on this axis. - * @property-read float $maxValue - * Maximum value to display on this axis. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementLogarithmicalAxis extends ezcGraphChartElementAxis -{ - - /** - * Constant used for calculation of automatic definition of major scaling - * steps - */ - const MAX_STEPS = 9; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['min'] = null; - $this->properties['max'] = null; - $this->properties['base'] = 10; - $this->properties['logarithmicalFormatString'] = '%1$d^%2$d'; - $this->properties['minValue'] = null; - $this->properties['maxValue'] = null; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'min': - case 'max': - if ( !is_numeric( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - $this->properties['initialized'] = true; - break; - case 'base': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - case 'logarithmicalFormatString': - $this->properties['logarithmicalFormatString'] = (string) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Add data for this axis - * - * @param array $values Value which will be displayed on this axis - * @return void - */ - public function addData( array $values ) - { - foreach ( $values as $value ) - { - if ( $this->properties['minValue'] === null || - $value < $this->properties['minValue'] ) - { - $this->properties['minValue'] = $value; - } - - if ( $this->properties['maxValue'] === null || - $value > $this->properties['maxValue'] ) - { - $this->properties['maxValue'] = $value; - } - } - - $this->properties['initialized'] = true; - } - - /** - * Calculate axis bounding values on base of the assigned values - * - * @abstract - * @access public - * @return void - */ - public function calculateAxisBoundings() - { - // Prevent division by zero, when min == max - if ( $this->properties['minValue'] == $this->properties['maxValue'] ) - { - if ( $this->properties['minValue'] == 0 ) - { - $this->properties['maxValue'] = 1; - } - else - { - $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 ); - $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 ); - } - } - - if ( $this->properties['minValue'] <= 0 ) - { - throw new ezcGraphOutOfLogithmicalBoundingsException( $this->properties['minValue'] ); - } - - // Use custom minimum and maximum if available - if ( $this->properties['min'] !== null ) - { - $this->properties['minValue'] = pow( $this->properties['base'], $this->properties['min'] ); - } - - if ( $this->properties['max'] !== null ) - { - $this->properties['maxValue'] = pow( $this->properties['base'], $this->properties['max'] ); - } - - // Calculate "nice" values for scaling parameters - if ( $this->properties['min'] === null ) - { - $this->properties['min'] = floor( log( $this->properties['minValue'], $this->properties['base'] ) ); - } - - if ( $this->properties['max'] === null ) - { - $this->properties['max'] = ceil( log( $this->properties['maxValue'], $this->properties['base'] ) ); - } - - $this->properties['minorStep'] = 1; - if ( ( $modifier = ( ( $this->properties['max'] - $this->properties['min'] ) / self::MAX_STEPS ) ) > 1 ) - { - $this->properties['majorStep'] = $modifier = ceil( $modifier ); - $this->properties['min'] = floor( $this->properties['min'] / $modifier ) * $modifier; - $this->properties['max'] = floor( $this->properties['max'] / $modifier ) * $modifier; - } - else - { - $this->properties['majorStep'] = 1; - } - } - - /** - * Get coordinate for a dedicated value on the chart - * - * @param float $value Value to determine position for - * @return float Position on chart - */ - public function getCoordinate( $value ) - { - // Force typecast, because ( false < -100 ) results in (bool) true - $floatValue = (float) $value; - - if ( $value === false ) - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return 0.; - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1.; - } - } - else - { - $position = ( log( $value, $this->properties['base'] ) - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); - - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return $position; - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1 - $position; - } - } - } - - /** - * Return count of minor steps - * - * @return integer Count of minor steps - */ - public function getMinorStepCount() - { - return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] ); - } - - /** - * Return count of major steps - * - * @return integer Count of major steps - */ - public function getMajorStepCount() - { - return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] ); - } - - /** - * Get label for a dedicated step on the axis - * - * @param integer $step Number of step - * @return string label - */ - public function getLabel( $step ) - { - if ( $this->properties['labelCallback'] !== null ) - { - return call_user_func_array( - $this->properties['labelCallback'], - array( - sprintf( - $this->properties['logarithmicalFormatString'], - $this->properties['base'], - $this->properties['min'] + ( $step * $this->properties['majorStep'] ) - ), - $step, - ) - ); - } - else - { - return sprintf( - $this->properties['logarithmicalFormatString'], - $this->properties['base'], - $this->properties['min'] + ( $step * $this->properties['majorStep'] ) - ); - } - } - - /** - * Is zero step - * - * Returns true if the given step is the one on the initial axis position - * - * @param int $step Number of step - * @return bool Status If given step is initial axis position - */ - public function isZeroStep( $step ) - { - return ( $step == 0 ); - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->title = 'The power of x'; + * $graph->legend->position = ezcGraph::BOTTOM; + * + * $graph->xAxis = new ezcGraphChartElementNumericAxis(); + * $graph->yAxis = new ezcGraphChartElementLogarithmicalAxis(); + * + * $graph->data['x^2'] = new ezcGraphNumericDataSet( + * -10, 10, + * create_function( '$x', 'return pow( $x, 2 ) + 1;' ) + * ); + * + * $graph->data['x^4'] = new ezcGraphNumericDataSet( + * -10, 10, + * create_function( '$x', 'return pow( $x, 4 ) + 1;' ) + * ); + * + * $graph->data['x^6'] = new ezcGraphNumericDataSet( + * -10, 10, + * create_function( '$x', 'return pow( $x, 6 ) + 1;' ) + * ); + * + * $graph->render( 400, 250, 'tutorial_axis_logarithmic.svg' ); + * + * + * @property float $base + * Base for logarithmical scaling. + * @property string $logarithmicalFormatString + * Sprintf formatstring for the axis labels where + * $1 is the base and + * $2 is the exponent. + * @property-read float $minValue + * Minimum Value to display on this axis. + * @property-read float $maxValue + * Maximum value to display on this axis. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementLogarithmicalAxis extends ezcGraphChartElementAxis +{ + + /** + * Constant used for calculation of automatic definition of major scaling + * steps + */ + const MAX_STEPS = 9; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['min'] = null; + $this->properties['max'] = null; + $this->properties['base'] = 10; + $this->properties['logarithmicalFormatString'] = '%1$d^%2$d'; + $this->properties['minValue'] = null; + $this->properties['maxValue'] = null; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'min': + case 'max': + if ( !is_numeric( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + $this->properties['initialized'] = true; + break; + case 'base': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + case 'logarithmicalFormatString': + $this->properties['logarithmicalFormatString'] = (string) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Add data for this axis + * + * @param array $values Value which will be displayed on this axis + * @return void + */ + public function addData( array $values ) + { + foreach ( $values as $value ) + { + if ( $this->properties['minValue'] === null || + $value < $this->properties['minValue'] ) + { + $this->properties['minValue'] = $value; + } + + if ( $this->properties['maxValue'] === null || + $value > $this->properties['maxValue'] ) + { + $this->properties['maxValue'] = $value; + } + } + + $this->properties['initialized'] = true; + } + + /** + * Calculate axis bounding values on base of the assigned values + * + * @abstract + * @access public + * @return void + */ + public function calculateAxisBoundings() + { + // Prevent division by zero, when min == max + if ( $this->properties['minValue'] == $this->properties['maxValue'] ) + { + if ( $this->properties['minValue'] == 0 ) + { + $this->properties['maxValue'] = 1; + } + else + { + $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 ); + $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 ); + } + } + + if ( $this->properties['minValue'] <= 0 ) + { + throw new ezcGraphOutOfLogithmicalBoundingsException( $this->properties['minValue'] ); + } + + // Use custom minimum and maximum if available + if ( $this->properties['min'] !== null ) + { + $this->properties['minValue'] = pow( $this->properties['base'], $this->properties['min'] ); + } + + if ( $this->properties['max'] !== null ) + { + $this->properties['maxValue'] = pow( $this->properties['base'], $this->properties['max'] ); + } + + // Calculate "nice" values for scaling parameters + if ( $this->properties['min'] === null ) + { + $this->properties['min'] = floor( log( $this->properties['minValue'], $this->properties['base'] ) ); + } + + if ( $this->properties['max'] === null ) + { + $this->properties['max'] = ceil( log( $this->properties['maxValue'], $this->properties['base'] ) ); + } + + $this->properties['minorStep'] = 1; + if ( ( $modifier = ( ( $this->properties['max'] - $this->properties['min'] ) / self::MAX_STEPS ) ) > 1 ) + { + $this->properties['majorStep'] = $modifier = ceil( $modifier ); + $this->properties['min'] = floor( $this->properties['min'] / $modifier ) * $modifier; + $this->properties['max'] = floor( $this->properties['max'] / $modifier ) * $modifier; + } + else + { + $this->properties['majorStep'] = 1; + } + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param float $value Value to determine position for + * @return float Position on chart + */ + public function getCoordinate( $value ) + { + // Force typecast, because ( false < -100 ) results in (bool) true + $floatValue = (float) $value; + + if ( $value === false ) + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return 0.; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1.; + } + } + else + { + $position = ( log( $value, $this->properties['base'] ) - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); + + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return $position; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1 - $position; + } + } + } + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + public function getMinorStepCount() + { + return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] ); + } + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + public function getMajorStepCount() + { + return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] ); + } + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + public function getLabel( $step ) + { + if ( $this->properties['labelCallback'] !== null ) + { + return call_user_func_array( + $this->properties['labelCallback'], + array( + sprintf( + $this->properties['logarithmicalFormatString'], + $this->properties['base'], + $this->properties['min'] + ( $step * $this->properties['majorStep'] ) + ), + $step, + ) + ); + } + else + { + return sprintf( + $this->properties['logarithmicalFormatString'], + $this->properties['base'], + $this->properties['min'] + ( $step * $this->properties['majorStep'] ) + ); + } + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + public function isZeroStep( $step ) + { + return ( $step == 0 ); + } +} + +?> diff --git a/library/ezc/Graph/src/axis/numeric.php b/library/ezc/Graph/src/axis/numeric.php index f3e2117579..ce38b45abf 100644 --- a/library/ezc/Graph/src/axis/numeric.php +++ b/library/ezc/Graph/src/axis/numeric.php @@ -1,501 +1,501 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->title = 'Some random data'; - * $graph->legend = false; - * - * $graph->xAxis = new ezcGraphChartElementNumericAxis(); - * // The y axis is numeric by default. - * - * $graph->xAxis->min = -15; - * $graph->xAxis->max = 15; - * $graph->xAxis->majorStep = 5; - * - * $data = array( - * array(), - * array() - * ); - * for ( $i = -10; $i <= 10; $i++ ) - * { - * $data[0][$i] = mt_rand( -23, 59 ); - * $data[1][$i] = mt_rand( -23, 59 ); - * } - * - * // Add data - * $graph->data['random blue'] = new ezcGraphArrayDataSet( $data[0] ); - * $graph->data['random green'] = new ezcGraphArrayDataSet( $data[1] ); - * - * $graph->render( 400, 150, 'tutorial_axis_numeric.svg' ); - * - * - * @property float $min - * Minimum value of displayed scale on axis. - * @property float $max - * Maximum value of displayed scale on axis. - * @property mixed $majorStep - * Labeled major steps displayed on the axis. - * @property mixed $minorStep - * Non labeled minor steps on the axis. - * @property-read float $minValue - * Minimum Value to display on this axis. - * @property-read float $maxValue - * Maximum value to display on this axis. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementNumericAxis extends ezcGraphChartElementAxis -{ - - /** - * Constant used for calculation of automatic definition of major scaling - * steps - */ - const MIN_MAJOR_COUNT = 5; - - /** - * Constant used for automatic calculation of minor steps from given major - * steps - */ - const MIN_MINOR_COUNT = 8; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['min'] = null; - $this->properties['max'] = null; - $this->properties['minValue'] = null; - $this->properties['maxValue'] = null; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'min': - if ( !is_numeric( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); - } - - $this->properties['min'] = (float) $propertyValue; - $this->properties['initialized'] = true; - break; - case 'max': - if ( !is_numeric( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); - } - - $this->properties['max'] = (float) $propertyValue; - $this->properties['initialized'] = true; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Returns a "nice" number for a given floating point number. - * - * Nice numbers are steps on a scale which are easily recognized by humans - * like 0.5, 25, 1000 etc. - * - * @param float $float Number to be altered - * @return float Nice number - */ - protected function getNiceNumber( $float ) - { - // Get absolute value and save sign - $abs = abs( $float ); - $sign = $float / $abs; - - // Normalize number to a range between 1 and 10 - $log = (int) round( log10( $abs ), 0 ); - $abs /= pow( 10, $log ); - - - // find next nice number - if ( $abs > 5 ) - { - $abs = 10.; - } - elseif ( $abs > 2.5 ) - { - $abs = 5.; - } - elseif ( $abs > 1 ) - { - $abs = 2.5; - } - else - { - $abs = 1; - } - - // unnormalize number to original values - return $abs * pow( 10, $log ) * $sign; - } - - /** - * Calculate minimum value for displayed axe basing on real minimum and - * major step size - * - * @param float $min Real data minimum - * @param float $max Real data maximum - * @return void - */ - protected function calculateMinimum( $min, $max ) - { - if ( $this->properties['max'] === null ) - { - $this->properties['min'] = floor( $min / $this->properties['majorStep'] ) * $this->properties['majorStep']; - } - else - { - $calculatedMin = $this->properties['max']; - - do { - $calculatedMin -= $this->properties['majorStep']; - } while ( $calculatedMin > $min ); - - $this->properties['min'] = $calculatedMin; - } - } - - /** - * Calculate maximum value for displayed axe basing on real maximum and - * major step size - * - * @param float $min Real data minimum - * @param float $max Real data maximum - * @return void - */ - protected function calculateMaximum( $min, $max ) - { - $calculatedMax = $this->properties['min']; - - do { - $calculatedMax += $this->properties['majorStep']; - } while ( $calculatedMax < $max ); - - $this->properties['max'] = $calculatedMax; - } - - /** - * Calculate size of minor steps based on the size of the major step size - * - * @param float $min Real data minimum - * @param float $max Real data maximum - * @return void - */ - protected function calculateMinorStep( $min, $max ) - { - $stepSize = $this->properties['majorStep'] / self::MIN_MINOR_COUNT; - $this->properties['minorStep'] = $this->getNiceNumber( $stepSize ); - } - - /** - * Calculate size of major step based on the span to be displayed and the - * defined MIN_MAJOR_COUNT constant. - * - * @param float $min Real data minimum - * @param float $max Real data maximum - * @return void - */ - protected function calculateMajorStep( $min, $max ) - { - $span = $max - $min; - $stepSize = $span / self::MIN_MAJOR_COUNT; - $this->properties['majorStep'] = $this->getNiceNumber( $stepSize ); - } - - /** - * Add data for this axis - * - * @param array $values Value which will be displayed on this axis - * @return void - */ - public function addData( array $values ) - { - foreach ( $values as $value ) - { - if ( $this->properties['minValue'] === null || - $value < $this->properties['minValue'] ) - { - $this->properties['minValue'] = $value; - } - - if ( $this->properties['maxValue'] === null || - $value > $this->properties['maxValue'] ) - { - $this->properties['maxValue'] = $value; - } - } - - $this->properties['initialized'] = true; - } - - /** - * Calculate axis bounding values on base of the assigned values - * - * @abstract - * @access public - * @return void - */ - public function calculateAxisBoundings() - { - // Prevent division by zero, when min == max - if ( $this->properties['minValue'] == $this->properties['maxValue'] ) - { - if ( $this->properties['minValue'] == 0 ) - { - $this->properties['maxValue'] = 1; - } - else - { - if ( $this->properties['majorStep'] !== null ) - { - $this->properties['minValue'] -= $this->properties['majorStep']; - $this->properties['maxValue'] += $this->properties['majorStep']; - } - else - { - $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 ); - $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 ); - } - } - } - - // Use custom minimum and maximum if available - if ( $this->properties['min'] !== null ) - { - $this->properties['minValue'] = $this->properties['min']; - } - - if ( $this->properties['max'] !== null ) - { - $this->properties['maxValue'] = $this->properties['max']; - } - - // If min and max values are forced, we may not be able to find a - // "nice" number for the steps. Try to find such a nice step size, or - // fall back to a step size, which is just the span divided by 5. - if ( ( $this->properties['min'] !== null ) && - ( $this->properties['max'] !== null ) && - ( $this->properties['majorStep'] === null ) ) - { - $diff = $this->properties['max'] - $this->properties['min']; - $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); - $stepInvariance = $diff / $this->properties['majorStep']; - if ( ( $stepInvariance - floor( $stepInvariance ) ) > .0000001 ) - { - // For too big step invariances calculate the step size just - // from the given difference between min and max value. - $this->properties['majorStep'] = ( $this->properties['max'] - $this->properties['min'] ) / self::MIN_MAJOR_COUNT; - $this->properties['minorStep'] = $this->properties['majorStep'] / self::MIN_MAJOR_COUNT; - } - } - - // Calculate "nice" values for scaling parameters - if ( $this->properties['majorStep'] === null ) - { - $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); - } - - if ( $this->properties['minorStep'] === null ) - { - $this->calculateMinorStep( $this->properties['minValue'], $this->properties['maxValue'] ); - } - - if ( $this->properties['min'] === null ) - { - $this->calculateMinimum( $this->properties['minValue'], $this->properties['maxValue'] ); - } - - if ( $this->properties['max'] === null ) - { - $this->calculateMaximum( $this->properties['minValue'], $this->properties['maxValue'] ); - } - - // Check that the major step size matches up with the min and max - // values on the axis. - $quotient = ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep']; - $quotient = abs( $quotient - floor( $quotient ) ); - if ( ( $quotient >= .00001 ) && - ( abs( $quotient - 1 ) >= .00001 ) ) - { - throw new ezcGraphInvalidStepSizeException( "The difference between minimum and maximum value is not a multiplier of the major step size." ); - } - - // Check that the minor step size matches up with major step size on - // the axis. - $quotient = $this->properties['majorStep'] / $this->properties['minorStep']; - $quotient = abs( $quotient - floor( $quotient ) ); - if ( ( $quotient >= .00001 ) && - ( abs( $quotient - 1 ) >= .00001 ) ) - { - throw new ezcGraphInvalidStepSizeException( "The major step size value is not a multiplier of the minor step size." ); - } - } - - /** - * Get coordinate for a dedicated value on the chart - * - * @param float $value Value to determine position for - * @return float Position on chart - */ - public function getCoordinate( $value ) - { - // Force typecast, because ( false < -100 ) results in (bool) true - $floatValue = (float) $value; - - if ( ( $value === false ) && - ( ( $floatValue < $this->properties['min'] ) || ( $floatValue > $this->properties['max'] ) ) ) - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return 0.; - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1.; - } - } - else - { - switch ( $this->position ) - { - case ezcGraph::LEFT: - case ezcGraph::TOP: - return ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); - case ezcGraph::RIGHT: - case ezcGraph::BOTTOM: - return 1 - ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); - } - } - } - - /** - * Return count of minor steps - * - * @return integer Count of minor steps - */ - public function getMinorStepCount() - { - return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] ); - } - - /** - * Return count of major steps - * - * @return integer Count of major steps - */ - public function getMajorStepCount() - { - return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] ); - } - - /** - * Get label for a dedicated step on the axis - * - * @param integer $step Number of step - * @return string label - */ - public function getLabel( $step ) - { - if ( $this->properties['labelCallback'] !== null ) - { - return call_user_func_array( - $this->properties['labelCallback'], - array( - $this->properties['min'] + ( $step * $this->properties['majorStep'] ), - $step, - ) - ); - } - else - { - return $this->properties['min'] + ( $step * $this->properties['majorStep'] ); - } - } - - /** - * Is zero step - * - * Returns true if the given step is the one on the initial axis position - * - * @param int $step Number of step - * @return bool Status If given step is initial axis position - */ - public function isZeroStep( $step ) - { - return ( $this->getLabel( $step ) == 0 ); - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->title = 'Some random data'; + * $graph->legend = false; + * + * $graph->xAxis = new ezcGraphChartElementNumericAxis(); + * // The y axis is numeric by default. + * + * $graph->xAxis->min = -15; + * $graph->xAxis->max = 15; + * $graph->xAxis->majorStep = 5; + * + * $data = array( + * array(), + * array() + * ); + * for ( $i = -10; $i <= 10; $i++ ) + * { + * $data[0][$i] = mt_rand( -23, 59 ); + * $data[1][$i] = mt_rand( -23, 59 ); + * } + * + * // Add data + * $graph->data['random blue'] = new ezcGraphArrayDataSet( $data[0] ); + * $graph->data['random green'] = new ezcGraphArrayDataSet( $data[1] ); + * + * $graph->render( 400, 150, 'tutorial_axis_numeric.svg' ); + * + * + * @property float $min + * Minimum value of displayed scale on axis. + * @property float $max + * Maximum value of displayed scale on axis. + * @property mixed $majorStep + * Labeled major steps displayed on the axis. + * @property mixed $minorStep + * Non labeled minor steps on the axis. + * @property-read float $minValue + * Minimum Value to display on this axis. + * @property-read float $maxValue + * Maximum value to display on this axis. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementNumericAxis extends ezcGraphChartElementAxis +{ + + /** + * Constant used for calculation of automatic definition of major scaling + * steps + */ + const MIN_MAJOR_COUNT = 5; + + /** + * Constant used for automatic calculation of minor steps from given major + * steps + */ + const MIN_MINOR_COUNT = 8; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['min'] = null; + $this->properties['max'] = null; + $this->properties['minValue'] = null; + $this->properties['maxValue'] = null; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'min': + if ( !is_numeric( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); + } + + $this->properties['min'] = (float) $propertyValue; + $this->properties['initialized'] = true; + break; + case 'max': + if ( !is_numeric( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); + } + + $this->properties['max'] = (float) $propertyValue; + $this->properties['initialized'] = true; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Returns a "nice" number for a given floating point number. + * + * Nice numbers are steps on a scale which are easily recognized by humans + * like 0.5, 25, 1000 etc. + * + * @param float $float Number to be altered + * @return float Nice number + */ + protected function getNiceNumber( $float ) + { + // Get absolute value and save sign + $abs = abs( $float ); + $sign = $float / $abs; + + // Normalize number to a range between 1 and 10 + $log = (int) round( log10( $abs ), 0 ); + $abs /= pow( 10, $log ); + + + // find next nice number + if ( $abs > 5 ) + { + $abs = 10.; + } + elseif ( $abs > 2.5 ) + { + $abs = 5.; + } + elseif ( $abs > 1 ) + { + $abs = 2.5; + } + else + { + $abs = 1; + } + + // unnormalize number to original values + return $abs * pow( 10, $log ) * $sign; + } + + /** + * Calculate minimum value for displayed axe basing on real minimum and + * major step size + * + * @param float $min Real data minimum + * @param float $max Real data maximum + * @return void + */ + protected function calculateMinimum( $min, $max ) + { + if ( $this->properties['max'] === null ) + { + $this->properties['min'] = floor( $min / $this->properties['majorStep'] ) * $this->properties['majorStep']; + } + else + { + $calculatedMin = $this->properties['max']; + + do { + $calculatedMin -= $this->properties['majorStep']; + } while ( $calculatedMin > $min ); + + $this->properties['min'] = $calculatedMin; + } + } + + /** + * Calculate maximum value for displayed axe basing on real maximum and + * major step size + * + * @param float $min Real data minimum + * @param float $max Real data maximum + * @return void + */ + protected function calculateMaximum( $min, $max ) + { + $calculatedMax = $this->properties['min']; + + do { + $calculatedMax += $this->properties['majorStep']; + } while ( $calculatedMax < $max ); + + $this->properties['max'] = $calculatedMax; + } + + /** + * Calculate size of minor steps based on the size of the major step size + * + * @param float $min Real data minimum + * @param float $max Real data maximum + * @return void + */ + protected function calculateMinorStep( $min, $max ) + { + $stepSize = $this->properties['majorStep'] / self::MIN_MINOR_COUNT; + $this->properties['minorStep'] = $this->getNiceNumber( $stepSize ); + } + + /** + * Calculate size of major step based on the span to be displayed and the + * defined MIN_MAJOR_COUNT constant. + * + * @param float $min Real data minimum + * @param float $max Real data maximum + * @return void + */ + protected function calculateMajorStep( $min, $max ) + { + $span = $max - $min; + $stepSize = $span / self::MIN_MAJOR_COUNT; + $this->properties['majorStep'] = $this->getNiceNumber( $stepSize ); + } + + /** + * Add data for this axis + * + * @param array $values Value which will be displayed on this axis + * @return void + */ + public function addData( array $values ) + { + foreach ( $values as $value ) + { + if ( $this->properties['minValue'] === null || + $value < $this->properties['minValue'] ) + { + $this->properties['minValue'] = $value; + } + + if ( $this->properties['maxValue'] === null || + $value > $this->properties['maxValue'] ) + { + $this->properties['maxValue'] = $value; + } + } + + $this->properties['initialized'] = true; + } + + /** + * Calculate axis bounding values on base of the assigned values + * + * @abstract + * @access public + * @return void + */ + public function calculateAxisBoundings() + { + // Prevent division by zero, when min == max + if ( $this->properties['minValue'] == $this->properties['maxValue'] ) + { + if ( $this->properties['minValue'] == 0 ) + { + $this->properties['maxValue'] = 1; + } + else + { + if ( $this->properties['majorStep'] !== null ) + { + $this->properties['minValue'] -= $this->properties['majorStep']; + $this->properties['maxValue'] += $this->properties['majorStep']; + } + else + { + $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 ); + $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 ); + } + } + } + + // Use custom minimum and maximum if available + if ( $this->properties['min'] !== null ) + { + $this->properties['minValue'] = $this->properties['min']; + } + + if ( $this->properties['max'] !== null ) + { + $this->properties['maxValue'] = $this->properties['max']; + } + + // If min and max values are forced, we may not be able to find a + // "nice" number for the steps. Try to find such a nice step size, or + // fall back to a step size, which is just the span divided by 5. + if ( ( $this->properties['min'] !== null ) && + ( $this->properties['max'] !== null ) && + ( $this->properties['majorStep'] === null ) ) + { + $diff = $this->properties['max'] - $this->properties['min']; + $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); + $stepInvariance = $diff / $this->properties['majorStep']; + if ( ( $stepInvariance - floor( $stepInvariance ) ) > .0000001 ) + { + // For too big step invariances calculate the step size just + // from the given difference between min and max value. + $this->properties['majorStep'] = ( $this->properties['max'] - $this->properties['min'] ) / self::MIN_MAJOR_COUNT; + $this->properties['minorStep'] = $this->properties['majorStep'] / self::MIN_MAJOR_COUNT; + } + } + + // Calculate "nice" values for scaling parameters + if ( $this->properties['majorStep'] === null ) + { + $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] ); + } + + if ( $this->properties['minorStep'] === null ) + { + $this->calculateMinorStep( $this->properties['minValue'], $this->properties['maxValue'] ); + } + + if ( $this->properties['min'] === null ) + { + $this->calculateMinimum( $this->properties['minValue'], $this->properties['maxValue'] ); + } + + if ( $this->properties['max'] === null ) + { + $this->calculateMaximum( $this->properties['minValue'], $this->properties['maxValue'] ); + } + + // Check that the major step size matches up with the min and max + // values on the axis. + $quotient = ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep']; + $quotient = abs( $quotient - floor( $quotient ) ); + if ( ( $quotient >= .00001 ) && + ( abs( $quotient - 1 ) >= .00001 ) ) + { + throw new ezcGraphInvalidStepSizeException( "The difference between minimum and maximum value is not a multiplier of the major step size." ); + } + + // Check that the minor step size matches up with major step size on + // the axis. + $quotient = $this->properties['majorStep'] / $this->properties['minorStep']; + $quotient = abs( $quotient - floor( $quotient ) ); + if ( ( $quotient >= .00001 ) && + ( abs( $quotient - 1 ) >= .00001 ) ) + { + throw new ezcGraphInvalidStepSizeException( "The major step size value is not a multiplier of the minor step size." ); + } + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param float $value Value to determine position for + * @return float Position on chart + */ + public function getCoordinate( $value ) + { + // Force typecast, because ( false < -100 ) results in (bool) true + $floatValue = (float) $value; + + if ( ( $value === false ) && + ( ( $floatValue < $this->properties['min'] ) || ( $floatValue > $this->properties['max'] ) ) ) + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return 0.; + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1.; + } + } + else + { + switch ( $this->position ) + { + case ezcGraph::LEFT: + case ezcGraph::TOP: + return ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); + case ezcGraph::RIGHT: + case ezcGraph::BOTTOM: + return 1 - ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] ); + } + } + } + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + public function getMinorStepCount() + { + return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] ); + } + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + public function getMajorStepCount() + { + return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] ); + } + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + public function getLabel( $step ) + { + if ( $this->properties['labelCallback'] !== null ) + { + return call_user_func_array( + $this->properties['labelCallback'], + array( + $this->properties['min'] + ( $step * $this->properties['majorStep'] ), + $step, + ) + ); + } + else + { + return $this->properties['min'] + ( $step * $this->properties['majorStep'] ); + } + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + public function isZeroStep( $step ) + { + return ( $this->getLabel( $step ) == 0 ); + } +} + +?> diff --git a/library/ezc/Graph/src/charts/bar.php b/library/ezc/Graph/src/charts/bar.php index b3be14a155..2806b21dbd 100644 --- a/library/ezc/Graph/src/charts/bar.php +++ b/library/ezc/Graph/src/charts/bar.php @@ -1,94 +1,94 @@ - - * // Create a new line chart - * $chart = new ezcGraphBarChart(); - * - * // Add data to line chart - * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( - * array( - * '100' => 1.2, - * '200' => 43.2, - * '300' => -34.14, - * '350' => 65, - * '400' => 123, - * ) - * ); - * - * // Render chart with default 2d renderer and default SVG driver - * $chart->render( 500, 200, 'bar_chart.svg' ); - * - * - * Each chart consists of several chart elements which represents logical - * parts of the chart and can be formatted independently. The bar chart - * consists of: - * - title ( {@link ezcGraphChartElementText} ) - * - legend ( {@link ezcGraphChartElementLegend} ) - * - background ( {@link ezcGraphChartElementBackground} ) - * - xAxis ( {@link ezcGraphChartElementLabeledAxis} ) - * - yAxis ( {@link ezcGraphChartElementNumericAxis} ) - * - * The type of the axis may be changed and all elements can be configured by - * accessing them as properties of the chart: - * - * - * $chart->legend->position = ezcGraph::RIGHT; - * - * - * The chart itself also offers several options to configure the appearance. As - * bar charts extend line charts the the extended configure options are - * available in {@link ezcGraphLineChartOptions} extending the - * {@link ezcGraphChartOptions}. - * - * @property ezcGraphLineChartOptions $options - * Chart options class - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphBarChart extends ezcGraphLineChart -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - parent::__construct(); - - $this->elements['xAxis']->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer(); - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - public function getDefaultDisplayType() - { - return ezcGraph::BAR; - } -} -?> + + * // Create a new line chart + * $chart = new ezcGraphBarChart(); + * + * // Add data to line chart + * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( + * array( + * '100' => 1.2, + * '200' => 43.2, + * '300' => -34.14, + * '350' => 65, + * '400' => 123, + * ) + * ); + * + * // Render chart with default 2d renderer and default SVG driver + * $chart->render( 500, 200, 'bar_chart.svg' ); + * + * + * Each chart consists of several chart elements which represents logical + * parts of the chart and can be formatted independently. The bar chart + * consists of: + * - title ( {@link ezcGraphChartElementText} ) + * - legend ( {@link ezcGraphChartElementLegend} ) + * - background ( {@link ezcGraphChartElementBackground} ) + * - xAxis ( {@link ezcGraphChartElementLabeledAxis} ) + * - yAxis ( {@link ezcGraphChartElementNumericAxis} ) + * + * The type of the axis may be changed and all elements can be configured by + * accessing them as properties of the chart: + * + * + * $chart->legend->position = ezcGraph::RIGHT; + * + * + * The chart itself also offers several options to configure the appearance. As + * bar charts extend line charts the the extended configure options are + * available in {@link ezcGraphLineChartOptions} extending the + * {@link ezcGraphChartOptions}. + * + * @property ezcGraphLineChartOptions $options + * Chart options class + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphBarChart extends ezcGraphLineChart +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + parent::__construct(); + + $this->elements['xAxis']->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer(); + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + public function getDefaultDisplayType() + { + return ezcGraph::BAR; + } +} +?> diff --git a/library/ezc/Graph/src/charts/line.php b/library/ezc/Graph/src/charts/line.php index 8ebec672d4..e95b36ee51 100644 --- a/library/ezc/Graph/src/charts/line.php +++ b/library/ezc/Graph/src/charts/line.php @@ -1,715 +1,715 @@ - - * // Create a new line chart - * $chart = new ezcGraphLineChart(); - * - * // Add data to line chart - * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( - * array( - * '100' => 1.2, - * '200' => 43.2, - * '300' => -34.14, - * '350' => 65, - * '400' => 123, - * ) - * ); - * - * // Render chart with default 2d renderer and default SVG driver - * $chart->render( 500, 200, 'line_chart.svg' ); - * - * - * Each chart consists of several chart elements which represents logical - * parts of the chart and can be formatted independently. The line chart - * consists of: - * - title ( {@link ezcGraphChartElementText} ) - * - legend ( {@link ezcGraphChartElementLegend} ) - * - background ( {@link ezcGraphChartElementBackground} ) - * - xAxis ( {@link ezcGraphChartElementLabeledAxis} ) - * - yAxis ( {@link ezcGraphChartElementNumericAxis} ) - * - * The type of the axis may be changed and all elements can be configured by - * accessing them as properties of the chart: - * - * - * $chart->legend->position = ezcGraph::RIGHT; - * - * - * The chart itself also offers several options to configure the appearance. - * The extended configure options are available in - * {@link ezcGraphLineChartOptions} extending the {@link ezcGraphChartOptions}. - * - * @property ezcGraphLineChartOptions $options - * Chart options class - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphLineChart extends ezcGraphChart -{ - /** - * Array with additional axis for the chart - * - * @var ezcGraphAxisContainer - */ - protected $additionalAxis; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->additionalAxis = new ezcGraphAxisContainer( $this ); - - $this->options = new ezcGraphLineChartOptions( $options ); - $this->options->highlightFont = $this->options->font; - - parent::__construct(); - - $this->addElement( 'xAxis', new ezcGraphChartElementLabeledAxis() ); - $this->elements['xAxis']->position = ezcGraph::LEFT; - - $this->addElement( 'yAxis', new ezcGraphChartElementNumericAxis() ); - $this->elements['yAxis']->position = ezcGraph::BOTTOM; - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'additionalAxis': - return $this->additionalAxis; - } - - return parent::__get( $propertyName ); - } - - /** - * Options write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param mixed $propertyName Option name - * @param mixed $propertyValue Option value; - * @return mixed - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'xAxis': - if ( $propertyValue instanceof ezcGraphChartElementAxis ) - { - $this->addElement( 'xAxis', $propertyValue ); - $this->elements['xAxis']->position = ezcGraph::LEFT; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); - } - break; - case 'yAxis': - if ( $propertyValue instanceof ezcGraphChartElementAxis ) - { - $this->addElement( 'yAxis', $propertyValue ); - $this->elements['yAxis']->position = ezcGraph::BOTTOM; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); - } - break; - default: - parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Set colors and border for this element - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - foreach ( $this->additionalAxis as $element ) - { - $element->setFromPalette( $palette ); - } - - parent::setFromPalette( $palette ); - } - - /** - * Render the assigned data - * - * Will renderer all charts data in the remaining boundings after drawing - * all other chart elements. The data will be rendered depending on the - * settings in the dataset. - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Remaining boundings - * @return void - */ - protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings ) - { - // Use inner boundings for drawning chart data - $boundings = $innerBoundings; - - $yAxisNullPosition = $this->elements['yAxis']->getCoordinate( false ); - - // Initialize counters - $nr = array(); - $count = array(); - - foreach ( $this->data as $data ) - { - if ( !isset( $nr[$data->displayType->default] ) ) - { - $nr[$data->displayType->default] = 0; - $count[$data->displayType->default] = 0; - } - - $nr[$data->displayType->default]++; - $count[$data->displayType->default]++; - } - - $checkedRegularSteps = false; - - // Display data - foreach ( $this->data as $datasetName => $data ) - { - --$nr[$data->displayType->default]; - - // Check which axis should be used - $xAxis = ( $data->xAxis->default ? $data->xAxis->default: $this->elements['xAxis'] ); - $yAxis = ( $data->yAxis->default ? $data->yAxis->default: $this->elements['yAxis'] ); - - // Determine fill color for dataset - if ( $this->options->fillLines !== false ) - { - $fillColor = clone $data->color->default; - $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) ); - } - else - { - $fillColor = null; - } - - // Ensure regular steps on axis when used with bar charts and - // precalculate some values use to render bar charts - // - // Called only once and only when bars should be rendered - if ( ( $checkedRegularSteps === false ) && - ( $data->displayType->default === ezcGraph::BAR ) ) - { - $steps = $xAxis->getSteps(); - - $stepWidth = null; - foreach ( $steps as $step ) - { - if ( $stepWidth === null ) - { - $stepWidth = $step->width; - } - elseif ( $step->width !== $stepWidth ) - { - throw new ezcGraphUnregularStepsException(); - } - } - - $step = reset( $steps ); - if ( count( $step->childs ) ) - { - // Keep this for BC reasons - $barCount = ( $xAxis->getMajorStepCount() + 1 ) * ( $xAxis->getMinorStepCount() - 1 ); - $stepWidth = 1 / $barCount; - } - - $checkedRegularSteps = true; - $width = $xAxis->axisLabelRenderer->modifyChartDataPosition( - $yAxis->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - ( $boundings->x1 - $boundings->x0 ) * $stepWidth, - 0 - ) - ) - )->x; - } - - // Draw lines for dataset - $lastPoint = false; - foreach ( $data as $key => $value ) - { - // Calculate point in chart - $point = $xAxis->axisLabelRenderer->modifyChartDataPosition( - $yAxis->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - $xAxis->getCoordinate( $key ), - $yAxis->getCoordinate( $value ) - ) - ) - ); - - // Render depending on display type of dataset - switch ( true ) - { - case $data->displayType->default === ezcGraph::LINE: - $renderer->drawDataLine( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $data->color->default, - ( $lastPoint === false ? $point : $lastPoint ), - $point, - $nr[$data->displayType->default], - $count[$data->displayType->default], - $data->symbol[$key], - $data->color[$key], - $fillColor, - $yAxisNullPosition, - ( $data->lineThickness->default ? $data->lineThickness->default : $this->options->lineThickness ) - ); - - // Render highlight string if requested - if ( $data->highlight[$key] ) - { - $renderer->drawDataHighlightText( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $point, - $yAxisNullPosition, - $nr[$data->displayType->default], - $count[$data->displayType->default], - $this->options->highlightFont, - ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), - $this->options->highlightSize + $this->options->highlightFont->padding * 2, - ( $this->options->highlightLines ? $data->color[$key] : null ), - ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), - ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), - 0., - ezcGraph::LINE - ); - } - break; - case ( $data->displayType->default === ezcGraph::BAR ) && - $this->options->stackBars : - // Check if a bar has already been stacked - if ( !isset( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] ) ) - { - $start = new ezcGraphCoordinate( - $point->x, - $yAxisNullPosition - ); - - $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] = $value; - } - else - { - $start = $xAxis->axisLabelRenderer->modifyChartDataPosition( - $yAxis->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - $xAxis->getCoordinate( $key ), - $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] ) - ) - ) - ); - - $point = $xAxis->axisLabelRenderer->modifyChartDataPosition( - $yAxis->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - $xAxis->getCoordinate( $key ), - $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] += $value ) - ) - ) - ); - } - - // Force one symbol for each stacked bar - if ( !isset( $stackedSymbol[(int) ( $point->x * 10000 )] ) ) - { - $stackedSymbol[(int) ( $point->x * 10000 )] = $data->symbol[$key]; - } - - // Store stacked value for next iteration - $side = ( $point->y == 0 ? 1 : $point->y / abs( $point->y ) ); - $stacked[(int) ( $point->x * 10000 )][$side] = $point; - - $renderer->drawStackedBar( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $data->color->default, - $start, - $point, - $width, - $stackedSymbol[(int) ( $point->x * 10000 )], - $yAxisNullPosition - ); - - // Render highlight string if requested - if ( $data->highlight[$key] ) - { - $renderer->drawDataHighlightText( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $point, - $yAxisNullPosition, - $nr[$data->displayType->default], - $count[$data->displayType->default], - $this->options->highlightFont, - ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), - $this->options->highlightSize + $this->options->highlightFont->padding * 2, - ( $this->options->highlightLines ? $data->color[$key] : null ), - ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), - ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), - 0., - ezcGraph::LINE - ); - } - break; - case $data->displayType->default === ezcGraph::BAR: - $renderer->drawBar( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $data->color[$key], - $point, - $width, - $nr[$data->displayType->default], - $count[$data->displayType->default], - $data->symbol[$key], - $yAxisNullPosition - ); - - // Render highlight string if requested - if ( $data->highlight[$key] ) - { - $renderer->drawDataHighlightText( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $point, - $yAxisNullPosition, - $nr[$data->displayType->default], - $count[$data->displayType->default], - $this->options->highlightFont, - ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), - $this->options->highlightSize + $this->options->highlightFont->padding * 2, - ( $this->options->highlightLines ? $data->color[$key] : null ), - ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), - ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), - $width, - $data->displayType->default - ); - } - break; - default: - throw new ezcGraphInvalidDisplayTypeException( $data->displayType->default ); - break; - } - - // Store last point, used to connect lines in line chart. - $lastPoint = $point; - } - } - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - public function getDefaultDisplayType() - { - return ezcGraph::LINE; - } - - /** - * Check if renderer supports features requested by some special chart - * options. - * - * @throws ezcBaseValueException - * If some feature is not supported - * - * @return void - */ - protected function checkRenderer() - { - // When stacked bars are enabled, check if renderer supports them - if ( $this->options->stackBars ) - { - if ( !$this->renderer instanceof ezcGraphStackedBarsRenderer ) - { - throw new ezcBaseValueException( 'renderer', $this->renderer, 'ezcGraphStackedBarsRenderer' ); - } - } - } - - /** - * Aggregate and calculate value boundings on axis. - * - * @return void - */ - protected function setAxisValues() - { - // Virtual data set build for agrregated values sums for bar charts - $virtualBarSumDataSet = array( array(), array() ); - - // Calculate axis scaling and labeling - foreach ( $this->data as $dataset ) - { - $nr = 0; - $labels = array(); - $values = array(); - foreach ( $dataset as $label => $value ) - { - $labels[] = $label; - $values[] = $value; - - // Build sum of all bars - if ( $this->options->stackBars && - ( $dataset->displayType->default === ezcGraph::BAR ) ) - { - if ( !isset( $virtualBarSumDataSet[(int) $value >= 0][$nr] ) ) - { - $virtualBarSumDataSet[(int) $value >= 0][$nr++] = $value; - } - else - { - $virtualBarSumDataSet[(int) $value >= 0][$nr++] += $value; - } - } - } - - // Check if data has been associated with another custom axis, use - // default axis otherwise. - if ( $dataset->xAxis->default ) - { - $dataset->xAxis->default->addData( $labels ); - } - else - { - $this->elements['xAxis']->addData( $labels ); - } - - if ( $dataset->yAxis->default ) - { - $dataset->yAxis->default->addData( $values ); - } - else - { - $this->elements['yAxis']->addData( $values ); - } - } - - // Also use stacked bar values as base for y axis value span - // calculation - if ( $this->options->stackBars ) - { - $this->elements['yAxis']->addData( $virtualBarSumDataSet[0] ); - $this->elements['yAxis']->addData( $virtualBarSumDataSet[1] ); - } - - // There should always be something assigned to the main x and y axis. - if ( !$this->elements['xAxis']->initialized || - !$this->elements['yAxis']->initialized ) - { - throw new ezcGraphNoDataException(); - } - - // Calculate boundings from assigned data - $this->elements['xAxis']->calculateAxisBoundings(); - $this->elements['yAxis']->calculateAxisBoundings(); - } - - /** - * Renders the basic elements of this chart type - * - * @param int $width - * @param int $height - * @return void - */ - protected function renderElements( $width, $height ) - { - if ( !count( $this->data ) ) - { - throw new ezcGraphNoDataException(); - } - - // Check if renderer supports requested features - $this->checkRenderer(); - - // Set values form datasets on axis to calculate correct spans - $this->setAxisValues(); - - // Generate legend - $this->elements['legend']->generateFromDataSets( $this->data ); - - // Get boundings from parameters - $this->options->width = $width; - $this->options->height = $height; - - // Set image properties in driver - $this->driver->options->width = $width; - $this->driver->options->height = $height; - - // Render subelements - $boundings = new ezcGraphBoundings(); - $boundings->x1 = $this->options->width; - $boundings->y1 = $this->options->height; - - $boundings = $this->elements['xAxis']->axisLabelRenderer->modifyChartBoundings( - $this->elements['yAxis']->axisLabelRenderer->modifyChartBoundings( - $boundings, new ezcGraphCoordinate( 1, 0 ) - ), new ezcGraphCoordinate( -1, 0 ) - ); - - // Render subelements - foreach ( $this->elements as $name => $element ) - { - // Skip element, if it should not get rendered - if ( ( $this->renderElement[$name] === false ) || - ( $name === 'xAxis' ) || - ( $name === 'yAxis' ) ) - { - continue; - } - - $this->driver->options->font = $element->font; - $boundings = $element->render( $this->renderer, $boundings ); - } - - // Set relative positions of axis in chart depending on the "null" - // value on the other axis. - $this->elements['xAxis']->nullPosition = $this->elements['yAxis']->getCoordinate( false ); - $this->elements['yAxis']->nullPosition = $this->elements['xAxis']->getCoordinate( false ); - - // Calculate inner data boundings of chart - $innerBoundings = new ezcGraphBoundings( - $boundings->x0 + $boundings->width * - ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) || - ( $this->elements['xAxis']->position === ezcGraph::LEFT ) ) ? - $this->elements['yAxis']->axisSpace : - $this->elements['yAxis']->outerAxisSpace ), - $boundings->y0 + $boundings->height * - ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) || - ( $this->elements['yAxis']->position === ezcGraph::TOP ) ) ? - $this->elements['xAxis']->axisSpace : - $this->elements['yAxis']->outerAxisSpace ), - $boundings->x1 - $boundings->width * - ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) || - ( $this->elements['xAxis']->position === ezcGraph::RIGHT ) ) ? - $this->elements['yAxis']->axisSpace : - $this->elements['yAxis']->outerAxisSpace ), - $boundings->y1 - $boundings->height * - ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) || - ( $this->elements['yAxis']->position === ezcGraph::BOTTOM ) ) ? - $this->elements['xAxis']->axisSpace : - $this->elements['yAxis']->outerAxisSpace ) - ); - - // Render axis - $this->driver->options->font = $this->elements['yAxis']->font; - $boundings = $this->elements['xAxis']->render( $this->renderer, $boundings, $innerBoundings ); - $boundings = $this->elements['yAxis']->render( $this->renderer, $boundings, $innerBoundings ); - - // Render additional axis - foreach ( $this->additionalAxis as $element ) - { - if ( $element->initialized ) - { - // Calculate all required step sizes if values has been - // assigned to axis. - $element->calculateAxisBoundings(); - } - else - { - // Do not render any axis labels, if no values were assigned - // and no step sizes were defined. - $element->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer(); - } - - $this->driver->options->font = $element->font; - $element->nullPosition = $element->chartPosition; - $boundings = $element->render( $this->renderer, $boundings, $innerBoundings ); - } - - // Render graph - $this->renderData( $this->renderer, $boundings, $innerBoundings ); - } - - /** - * Render the line chart - * - * Renders the chart into a file or stream. The width and height are - * needed to specify the dimensions of the resulting image. For direct - * output use 'php://stdout' as output file. - * - * @param int $width Image width - * @param int $height Image height - * @param string $file Output file - * @apichange - * @return void - */ - public function render( $width, $height, $file = null ) - { - $this->renderElements( $width, $height ); - - if ( !empty( $file ) ) - { - $this->renderer->render( $file ); - } - - $this->renderedFile = $file; - } - - /** - * Renders this chart to direct output - * - * Does the same as ezcGraphChart::render(), but renders directly to - * output and not into a file. - * - * @param int $width - * @param int $height - * @apichange - * @return void - */ - public function renderToOutput( $width, $height ) - { - // @TODO: merge this function with render an deprecate ommit of third - // argument in render() when API break is possible - $this->renderElements( $width, $height ); - $this->renderer->render( null ); - } -} -?> + + * // Create a new line chart + * $chart = new ezcGraphLineChart(); + * + * // Add data to line chart + * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( + * array( + * '100' => 1.2, + * '200' => 43.2, + * '300' => -34.14, + * '350' => 65, + * '400' => 123, + * ) + * ); + * + * // Render chart with default 2d renderer and default SVG driver + * $chart->render( 500, 200, 'line_chart.svg' ); + * + * + * Each chart consists of several chart elements which represents logical + * parts of the chart and can be formatted independently. The line chart + * consists of: + * - title ( {@link ezcGraphChartElementText} ) + * - legend ( {@link ezcGraphChartElementLegend} ) + * - background ( {@link ezcGraphChartElementBackground} ) + * - xAxis ( {@link ezcGraphChartElementLabeledAxis} ) + * - yAxis ( {@link ezcGraphChartElementNumericAxis} ) + * + * The type of the axis may be changed and all elements can be configured by + * accessing them as properties of the chart: + * + * + * $chart->legend->position = ezcGraph::RIGHT; + * + * + * The chart itself also offers several options to configure the appearance. + * The extended configure options are available in + * {@link ezcGraphLineChartOptions} extending the {@link ezcGraphChartOptions}. + * + * @property ezcGraphLineChartOptions $options + * Chart options class + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphLineChart extends ezcGraphChart +{ + /** + * Array with additional axis for the chart + * + * @var ezcGraphAxisContainer + */ + protected $additionalAxis; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->additionalAxis = new ezcGraphAxisContainer( $this ); + + $this->options = new ezcGraphLineChartOptions( $options ); + $this->options->highlightFont = $this->options->font; + + parent::__construct(); + + $this->addElement( 'xAxis', new ezcGraphChartElementLabeledAxis() ); + $this->elements['xAxis']->position = ezcGraph::LEFT; + + $this->addElement( 'yAxis', new ezcGraphChartElementNumericAxis() ); + $this->elements['yAxis']->position = ezcGraph::BOTTOM; + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'additionalAxis': + return $this->additionalAxis; + } + + return parent::__get( $propertyName ); + } + + /** + * Options write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param mixed $propertyName Option name + * @param mixed $propertyValue Option value; + * @return mixed + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'xAxis': + if ( $propertyValue instanceof ezcGraphChartElementAxis ) + { + $this->addElement( 'xAxis', $propertyValue ); + $this->elements['xAxis']->position = ezcGraph::LEFT; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); + } + break; + case 'yAxis': + if ( $propertyValue instanceof ezcGraphChartElementAxis ) + { + $this->addElement( 'yAxis', $propertyValue ); + $this->elements['yAxis']->position = ezcGraph::BOTTOM; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); + } + break; + default: + parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Set colors and border for this element + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + foreach ( $this->additionalAxis as $element ) + { + $element->setFromPalette( $palette ); + } + + parent::setFromPalette( $palette ); + } + + /** + * Render the assigned data + * + * Will renderer all charts data in the remaining boundings after drawing + * all other chart elements. The data will be rendered depending on the + * settings in the dataset. + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Remaining boundings + * @return void + */ + protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings ) + { + // Use inner boundings for drawning chart data + $boundings = $innerBoundings; + + $yAxisNullPosition = $this->elements['yAxis']->getCoordinate( false ); + + // Initialize counters + $nr = array(); + $count = array(); + + foreach ( $this->data as $data ) + { + if ( !isset( $nr[$data->displayType->default] ) ) + { + $nr[$data->displayType->default] = 0; + $count[$data->displayType->default] = 0; + } + + $nr[$data->displayType->default]++; + $count[$data->displayType->default]++; + } + + $checkedRegularSteps = false; + + // Display data + foreach ( $this->data as $datasetName => $data ) + { + --$nr[$data->displayType->default]; + + // Check which axis should be used + $xAxis = ( $data->xAxis->default ? $data->xAxis->default: $this->elements['xAxis'] ); + $yAxis = ( $data->yAxis->default ? $data->yAxis->default: $this->elements['yAxis'] ); + + // Determine fill color for dataset + if ( $this->options->fillLines !== false ) + { + $fillColor = clone $data->color->default; + $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) ); + } + else + { + $fillColor = null; + } + + // Ensure regular steps on axis when used with bar charts and + // precalculate some values use to render bar charts + // + // Called only once and only when bars should be rendered + if ( ( $checkedRegularSteps === false ) && + ( $data->displayType->default === ezcGraph::BAR ) ) + { + $steps = $xAxis->getSteps(); + + $stepWidth = null; + foreach ( $steps as $step ) + { + if ( $stepWidth === null ) + { + $stepWidth = $step->width; + } + elseif ( $step->width !== $stepWidth ) + { + throw new ezcGraphUnregularStepsException(); + } + } + + $step = reset( $steps ); + if ( count( $step->childs ) ) + { + // Keep this for BC reasons + $barCount = ( $xAxis->getMajorStepCount() + 1 ) * ( $xAxis->getMinorStepCount() - 1 ); + $stepWidth = 1 / $barCount; + } + + $checkedRegularSteps = true; + $width = $xAxis->axisLabelRenderer->modifyChartDataPosition( + $yAxis->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + ( $boundings->x1 - $boundings->x0 ) * $stepWidth, + 0 + ) + ) + )->x; + } + + // Draw lines for dataset + $lastPoint = false; + foreach ( $data as $key => $value ) + { + // Calculate point in chart + $point = $xAxis->axisLabelRenderer->modifyChartDataPosition( + $yAxis->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + $xAxis->getCoordinate( $key ), + $yAxis->getCoordinate( $value ) + ) + ) + ); + + // Render depending on display type of dataset + switch ( true ) + { + case $data->displayType->default === ezcGraph::LINE: + $renderer->drawDataLine( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $data->color->default, + ( $lastPoint === false ? $point : $lastPoint ), + $point, + $nr[$data->displayType->default], + $count[$data->displayType->default], + $data->symbol[$key], + $data->color[$key], + $fillColor, + $yAxisNullPosition, + ( $data->lineThickness->default ? $data->lineThickness->default : $this->options->lineThickness ) + ); + + // Render highlight string if requested + if ( $data->highlight[$key] ) + { + $renderer->drawDataHighlightText( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $point, + $yAxisNullPosition, + $nr[$data->displayType->default], + $count[$data->displayType->default], + $this->options->highlightFont, + ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), + $this->options->highlightSize + $this->options->highlightFont->padding * 2, + ( $this->options->highlightLines ? $data->color[$key] : null ), + ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), + ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), + 0., + ezcGraph::LINE + ); + } + break; + case ( $data->displayType->default === ezcGraph::BAR ) && + $this->options->stackBars : + // Check if a bar has already been stacked + if ( !isset( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] ) ) + { + $start = new ezcGraphCoordinate( + $point->x, + $yAxisNullPosition + ); + + $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] = $value; + } + else + { + $start = $xAxis->axisLabelRenderer->modifyChartDataPosition( + $yAxis->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + $xAxis->getCoordinate( $key ), + $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] ) + ) + ) + ); + + $point = $xAxis->axisLabelRenderer->modifyChartDataPosition( + $yAxis->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + $xAxis->getCoordinate( $key ), + $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] += $value ) + ) + ) + ); + } + + // Force one symbol for each stacked bar + if ( !isset( $stackedSymbol[(int) ( $point->x * 10000 )] ) ) + { + $stackedSymbol[(int) ( $point->x * 10000 )] = $data->symbol[$key]; + } + + // Store stacked value for next iteration + $side = ( $point->y == 0 ? 1 : $point->y / abs( $point->y ) ); + $stacked[(int) ( $point->x * 10000 )][$side] = $point; + + $renderer->drawStackedBar( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $data->color->default, + $start, + $point, + $width, + $stackedSymbol[(int) ( $point->x * 10000 )], + $yAxisNullPosition + ); + + // Render highlight string if requested + if ( $data->highlight[$key] ) + { + $renderer->drawDataHighlightText( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $point, + $yAxisNullPosition, + $nr[$data->displayType->default], + $count[$data->displayType->default], + $this->options->highlightFont, + ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), + $this->options->highlightSize + $this->options->highlightFont->padding * 2, + ( $this->options->highlightLines ? $data->color[$key] : null ), + ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), + ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), + 0., + ezcGraph::LINE + ); + } + break; + case $data->displayType->default === ezcGraph::BAR: + $renderer->drawBar( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $data->color[$key], + $point, + $width, + $nr[$data->displayType->default], + $count[$data->displayType->default], + $data->symbol[$key], + $yAxisNullPosition + ); + + // Render highlight string if requested + if ( $data->highlight[$key] ) + { + $renderer->drawDataHighlightText( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $point, + $yAxisNullPosition, + $nr[$data->displayType->default], + $count[$data->displayType->default], + $this->options->highlightFont, + ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ), + $this->options->highlightSize + $this->options->highlightFont->padding * 2, + ( $this->options->highlightLines ? $data->color[$key] : null ), + ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ), + ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ), + $width, + $data->displayType->default + ); + } + break; + default: + throw new ezcGraphInvalidDisplayTypeException( $data->displayType->default ); + break; + } + + // Store last point, used to connect lines in line chart. + $lastPoint = $point; + } + } + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + public function getDefaultDisplayType() + { + return ezcGraph::LINE; + } + + /** + * Check if renderer supports features requested by some special chart + * options. + * + * @throws ezcBaseValueException + * If some feature is not supported + * + * @return void + */ + protected function checkRenderer() + { + // When stacked bars are enabled, check if renderer supports them + if ( $this->options->stackBars ) + { + if ( !$this->renderer instanceof ezcGraphStackedBarsRenderer ) + { + throw new ezcBaseValueException( 'renderer', $this->renderer, 'ezcGraphStackedBarsRenderer' ); + } + } + } + + /** + * Aggregate and calculate value boundings on axis. + * + * @return void + */ + protected function setAxisValues() + { + // Virtual data set build for agrregated values sums for bar charts + $virtualBarSumDataSet = array( array(), array() ); + + // Calculate axis scaling and labeling + foreach ( $this->data as $dataset ) + { + $nr = 0; + $labels = array(); + $values = array(); + foreach ( $dataset as $label => $value ) + { + $labels[] = $label; + $values[] = $value; + + // Build sum of all bars + if ( $this->options->stackBars && + ( $dataset->displayType->default === ezcGraph::BAR ) ) + { + if ( !isset( $virtualBarSumDataSet[(int) $value >= 0][$nr] ) ) + { + $virtualBarSumDataSet[(int) $value >= 0][$nr++] = $value; + } + else + { + $virtualBarSumDataSet[(int) $value >= 0][$nr++] += $value; + } + } + } + + // Check if data has been associated with another custom axis, use + // default axis otherwise. + if ( $dataset->xAxis->default ) + { + $dataset->xAxis->default->addData( $labels ); + } + else + { + $this->elements['xAxis']->addData( $labels ); + } + + if ( $dataset->yAxis->default ) + { + $dataset->yAxis->default->addData( $values ); + } + else + { + $this->elements['yAxis']->addData( $values ); + } + } + + // Also use stacked bar values as base for y axis value span + // calculation + if ( $this->options->stackBars ) + { + $this->elements['yAxis']->addData( $virtualBarSumDataSet[0] ); + $this->elements['yAxis']->addData( $virtualBarSumDataSet[1] ); + } + + // There should always be something assigned to the main x and y axis. + if ( !$this->elements['xAxis']->initialized || + !$this->elements['yAxis']->initialized ) + { + throw new ezcGraphNoDataException(); + } + + // Calculate boundings from assigned data + $this->elements['xAxis']->calculateAxisBoundings(); + $this->elements['yAxis']->calculateAxisBoundings(); + } + + /** + * Renders the basic elements of this chart type + * + * @param int $width + * @param int $height + * @return void + */ + protected function renderElements( $width, $height ) + { + if ( !count( $this->data ) ) + { + throw new ezcGraphNoDataException(); + } + + // Check if renderer supports requested features + $this->checkRenderer(); + + // Set values form datasets on axis to calculate correct spans + $this->setAxisValues(); + + // Generate legend + $this->elements['legend']->generateFromDataSets( $this->data ); + + // Get boundings from parameters + $this->options->width = $width; + $this->options->height = $height; + + // Set image properties in driver + $this->driver->options->width = $width; + $this->driver->options->height = $height; + + // Render subelements + $boundings = new ezcGraphBoundings(); + $boundings->x1 = $this->options->width; + $boundings->y1 = $this->options->height; + + $boundings = $this->elements['xAxis']->axisLabelRenderer->modifyChartBoundings( + $this->elements['yAxis']->axisLabelRenderer->modifyChartBoundings( + $boundings, new ezcGraphCoordinate( 1, 0 ) + ), new ezcGraphCoordinate( -1, 0 ) + ); + + // Render subelements + foreach ( $this->elements as $name => $element ) + { + // Skip element, if it should not get rendered + if ( ( $this->renderElement[$name] === false ) || + ( $name === 'xAxis' ) || + ( $name === 'yAxis' ) ) + { + continue; + } + + $this->driver->options->font = $element->font; + $boundings = $element->render( $this->renderer, $boundings ); + } + + // Set relative positions of axis in chart depending on the "null" + // value on the other axis. + $this->elements['xAxis']->nullPosition = $this->elements['yAxis']->getCoordinate( false ); + $this->elements['yAxis']->nullPosition = $this->elements['xAxis']->getCoordinate( false ); + + // Calculate inner data boundings of chart + $innerBoundings = new ezcGraphBoundings( + $boundings->x0 + $boundings->width * + ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) || + ( $this->elements['xAxis']->position === ezcGraph::LEFT ) ) ? + $this->elements['yAxis']->axisSpace : + $this->elements['yAxis']->outerAxisSpace ), + $boundings->y0 + $boundings->height * + ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) || + ( $this->elements['yAxis']->position === ezcGraph::TOP ) ) ? + $this->elements['xAxis']->axisSpace : + $this->elements['yAxis']->outerAxisSpace ), + $boundings->x1 - $boundings->width * + ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) || + ( $this->elements['xAxis']->position === ezcGraph::RIGHT ) ) ? + $this->elements['yAxis']->axisSpace : + $this->elements['yAxis']->outerAxisSpace ), + $boundings->y1 - $boundings->height * + ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) || + ( $this->elements['yAxis']->position === ezcGraph::BOTTOM ) ) ? + $this->elements['xAxis']->axisSpace : + $this->elements['yAxis']->outerAxisSpace ) + ); + + // Render axis + $this->driver->options->font = $this->elements['yAxis']->font; + $boundings = $this->elements['xAxis']->render( $this->renderer, $boundings, $innerBoundings ); + $boundings = $this->elements['yAxis']->render( $this->renderer, $boundings, $innerBoundings ); + + // Render additional axis + foreach ( $this->additionalAxis as $element ) + { + if ( $element->initialized ) + { + // Calculate all required step sizes if values has been + // assigned to axis. + $element->calculateAxisBoundings(); + } + else + { + // Do not render any axis labels, if no values were assigned + // and no step sizes were defined. + $element->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer(); + } + + $this->driver->options->font = $element->font; + $element->nullPosition = $element->chartPosition; + $boundings = $element->render( $this->renderer, $boundings, $innerBoundings ); + } + + // Render graph + $this->renderData( $this->renderer, $boundings, $innerBoundings ); + } + + /** + * Render the line chart + * + * Renders the chart into a file or stream. The width and height are + * needed to specify the dimensions of the resulting image. For direct + * output use 'php://stdout' as output file. + * + * @param int $width Image width + * @param int $height Image height + * @param string $file Output file + * @apichange + * @return void + */ + public function render( $width, $height, $file = null ) + { + $this->renderElements( $width, $height ); + + if ( !empty( $file ) ) + { + $this->renderer->render( $file ); + } + + $this->renderedFile = $file; + } + + /** + * Renders this chart to direct output + * + * Does the same as ezcGraphChart::render(), but renders directly to + * output and not into a file. + * + * @param int $width + * @param int $height + * @apichange + * @return void + */ + public function renderToOutput( $width, $height ) + { + // @TODO: merge this function with render an deprecate ommit of third + // argument in render() when API break is possible + $this->renderElements( $width, $height ); + $this->renderer->render( null ); + } +} +?> diff --git a/library/ezc/Graph/src/charts/odometer.php b/library/ezc/Graph/src/charts/odometer.php index 1b38a53186..4db240b1c3 100644 --- a/library/ezc/Graph/src/charts/odometer.php +++ b/library/ezc/Graph/src/charts/odometer.php @@ -1,296 +1,296 @@ - - * $graph = new ezcGraphOdometerChart(); - * $graph->title = 'Custom odometer'; - * - * $graph->data['data'] = new ezcGraphArrayDataSet( - * array( 87 ) - * ); - * - * // Set the marker color - * $graph->data['data']->color[0] = '#A0000055'; - * - * // Set colors for the background gradient - * $graph->options->startColor = '#2E3436'; - * $graph->options->endColor = '#EEEEEC'; - * - * // Define a border for the odometer - * $graph->options->borderWidth = 2; - * $graph->options->borderColor = '#BABDB6'; - * - * // Set marker width - * $graph->options->markerWidth = 5; - * - * // Set space, which the odometer may consume - * $graph->options->odometerHeight = .7; - * - * // Set axis span and label - * $graph->axis->min = 0; - * $graph->axis->max = 100; - * $graph->axis->label = 'Coverage '; - * - * $graph->render( 400, 150, 'custom_odometer_chart.svg' ); - * - * - * Each chart consists of several chart elements which represents logical parts - * of the chart and can be formatted independently. The odometer chart consists - * of: - * - title ( {@link ezcGraphChartElementText} ) - * - background ( {@link ezcGraphChartElementBackground} ) - * - * All elements can be configured by accessing them as properties of the chart: - * - * - * $chart->title->position = ezcGraph::BOTTOM; - * - * - * The chart itself also offers several options to configure the appearance. - * The extended configure options are available in - * {@link ezcGraphOdometerChartOptions} extending the {@link - * ezcGraphChartOptions}. - * - * @property ezcGraphOdometerChartOptions $options - * Chart options class - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphOdometerChart extends ezcGraphChart -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->options = new ezcGraphOdometerChartOptions( $options ); - - parent::__construct( $options ); - - $this->data = new ezcGraphChartSingleDataContainer( $this ); - - $this->addElement( 'axis', new ezcGraphChartElementNumericAxis()); - $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); - $this->elements['axis']->axisLabelRenderer->showZeroValue = true; - $this->elements['axis']->position = ezcGraph::LEFT; - $this->elements['axis']->axisSpace = .05; - } - - /** - * Property write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param string $propertyName Option name - * @param mixed $propertyValue Option value; - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'axis': - if ( $propertyValue instanceof ezcGraphChartElementAxis ) - { - $this->addElement( 'axis', $propertyValue ); - $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); - $this->elements['axis']->axisLabelRenderer->showZeroValue = true; - $this->elements['axis']->position = ezcGraph::LEFT; - $this->elements['axis']->axisSpace = .05; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); - } - break; - case 'renderer': - if ( $propertyValue instanceof ezcGraphOdometerRenderer ) - { - parent::__set( $propertyName, $propertyValue ); - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphOdometerRenderer' ); - } - break; - default: - parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Render the assigned data - * - * Will renderer all charts data in the remaining boundings after drawing - * all other chart elements. The data will be rendered depending on the - * settings in the dataset. - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Remaining boundings - * @return void - */ - protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - // Draw the odometer data - $dataset = $this->data->rewind(); - - foreach ( $dataset as $key => $value ) - { - $renderer->drawOdometerMarker( - $boundings, - $this->elements['axis']->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - $this->elements['axis']->getCoordinate( $value ), - 0 - ) - ), - $dataset->symbol[$key], - $dataset->color[$key], - $this->options->markerWidth - ); - } - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - public function getDefaultDisplayType() - { - return ezcGraph::ODOMETER; - } - - /** - * Renders the basic elements of this chart type - * - * @param int $width - * @param int $height - * @return void - */ - protected function renderElements( $width, $height ) - { - if ( !count( $this->data ) ) - { - throw new ezcGraphNoDataException(); - } - - // Set image properties in driver - $this->driver->options->width = $width; - $this->driver->options->height = $height; - - // no legend - $this->renderElement['legend'] = false; - - // Get boundings from parameters - $this->options->width = $width; - $this->options->height = $height; - - $boundings = new ezcGraphBoundings(); - $boundings->x1 = $this->options->width; - $boundings->y1 = $this->options->height; - - // Get values out the single used dataset to calculate axis boundings - $values = array(); - foreach ( $this->data->rewind() as $value ) - { - $values[] = $value; - } - - // Set values for Axis - $this->elements['axis']->addData( $values ); - $this->elements['axis']->nullPosition = 0.5 + $this->options->odometerHeight / 2; - $this->elements['axis']->calculateAxisBoundings(); - - // Render subelements exept axis, which will be drawn together with the - // odometer bar - foreach ( $this->elements as $name => $element ) - { - // Skip element, if it should not get rendered - if ( $this->renderElement[$name] === false || - $name === 'axis' ) - { - continue; - } - - $this->driver->options->font = $element->font; - $boundings = $element->render( $this->renderer, $boundings ); - } - - // Draw basic odometer - $this->driver->options->font = $this->elements['axis']->font; - $boundings = $this->renderer->drawOdometer( - $boundings, - $this->elements['axis'], - $this->options - ); - - // Render graph - $this->renderData( $this->renderer, $boundings ); - } - - /** - * Render the pie chart - * - * Renders the chart into a file or stream. The width and height are - * needed to specify the dimensions of the resulting image. For direct - * output use 'php://stdout' as output file. - * - * @param int $width Image width - * @param int $height Image height - * @param string $file Output file - * @apichange - * @return void - */ - public function render( $width, $height, $file = null ) - { - $this->renderElements( $width, $height ); - - if ( !empty( $file ) ) - { - $this->renderer->render( $file ); - } - - $this->renderedFile = $file; - } - - /** - * Renders this chart to direct output - * - * Does the same as ezcGraphChart::render(), but renders directly to - * output and not into a file. - * - * @param int $width - * @param int $height - * @apichange - * @return void - */ - public function renderToOutput( $width, $height ) - { - // @TODO: merge this function with render an deprecate ommit of third - // argument in render() when API break is possible - $this->renderElements( $width, $height ); - $this->renderer->render( null ); - } -} - -?> + + * $graph = new ezcGraphOdometerChart(); + * $graph->title = 'Custom odometer'; + * + * $graph->data['data'] = new ezcGraphArrayDataSet( + * array( 87 ) + * ); + * + * // Set the marker color + * $graph->data['data']->color[0] = '#A0000055'; + * + * // Set colors for the background gradient + * $graph->options->startColor = '#2E3436'; + * $graph->options->endColor = '#EEEEEC'; + * + * // Define a border for the odometer + * $graph->options->borderWidth = 2; + * $graph->options->borderColor = '#BABDB6'; + * + * // Set marker width + * $graph->options->markerWidth = 5; + * + * // Set space, which the odometer may consume + * $graph->options->odometerHeight = .7; + * + * // Set axis span and label + * $graph->axis->min = 0; + * $graph->axis->max = 100; + * $graph->axis->label = 'Coverage '; + * + * $graph->render( 400, 150, 'custom_odometer_chart.svg' ); + * + * + * Each chart consists of several chart elements which represents logical parts + * of the chart and can be formatted independently. The odometer chart consists + * of: + * - title ( {@link ezcGraphChartElementText} ) + * - background ( {@link ezcGraphChartElementBackground} ) + * + * All elements can be configured by accessing them as properties of the chart: + * + * + * $chart->title->position = ezcGraph::BOTTOM; + * + * + * The chart itself also offers several options to configure the appearance. + * The extended configure options are available in + * {@link ezcGraphOdometerChartOptions} extending the {@link + * ezcGraphChartOptions}. + * + * @property ezcGraphOdometerChartOptions $options + * Chart options class + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphOdometerChart extends ezcGraphChart +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->options = new ezcGraphOdometerChartOptions( $options ); + + parent::__construct( $options ); + + $this->data = new ezcGraphChartSingleDataContainer( $this ); + + $this->addElement( 'axis', new ezcGraphChartElementNumericAxis()); + $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); + $this->elements['axis']->axisLabelRenderer->showZeroValue = true; + $this->elements['axis']->position = ezcGraph::LEFT; + $this->elements['axis']->axisSpace = .05; + } + + /** + * Property write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param string $propertyName Option name + * @param mixed $propertyValue Option value; + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'axis': + if ( $propertyValue instanceof ezcGraphChartElementAxis ) + { + $this->addElement( 'axis', $propertyValue ); + $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); + $this->elements['axis']->axisLabelRenderer->showZeroValue = true; + $this->elements['axis']->position = ezcGraph::LEFT; + $this->elements['axis']->axisSpace = .05; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); + } + break; + case 'renderer': + if ( $propertyValue instanceof ezcGraphOdometerRenderer ) + { + parent::__set( $propertyName, $propertyValue ); + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphOdometerRenderer' ); + } + break; + default: + parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Render the assigned data + * + * Will renderer all charts data in the remaining boundings after drawing + * all other chart elements. The data will be rendered depending on the + * settings in the dataset. + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Remaining boundings + * @return void + */ + protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + // Draw the odometer data + $dataset = $this->data->rewind(); + + foreach ( $dataset as $key => $value ) + { + $renderer->drawOdometerMarker( + $boundings, + $this->elements['axis']->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + $this->elements['axis']->getCoordinate( $value ), + 0 + ) + ), + $dataset->symbol[$key], + $dataset->color[$key], + $this->options->markerWidth + ); + } + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + public function getDefaultDisplayType() + { + return ezcGraph::ODOMETER; + } + + /** + * Renders the basic elements of this chart type + * + * @param int $width + * @param int $height + * @return void + */ + protected function renderElements( $width, $height ) + { + if ( !count( $this->data ) ) + { + throw new ezcGraphNoDataException(); + } + + // Set image properties in driver + $this->driver->options->width = $width; + $this->driver->options->height = $height; + + // no legend + $this->renderElement['legend'] = false; + + // Get boundings from parameters + $this->options->width = $width; + $this->options->height = $height; + + $boundings = new ezcGraphBoundings(); + $boundings->x1 = $this->options->width; + $boundings->y1 = $this->options->height; + + // Get values out the single used dataset to calculate axis boundings + $values = array(); + foreach ( $this->data->rewind() as $value ) + { + $values[] = $value; + } + + // Set values for Axis + $this->elements['axis']->addData( $values ); + $this->elements['axis']->nullPosition = 0.5 + $this->options->odometerHeight / 2; + $this->elements['axis']->calculateAxisBoundings(); + + // Render subelements exept axis, which will be drawn together with the + // odometer bar + foreach ( $this->elements as $name => $element ) + { + // Skip element, if it should not get rendered + if ( $this->renderElement[$name] === false || + $name === 'axis' ) + { + continue; + } + + $this->driver->options->font = $element->font; + $boundings = $element->render( $this->renderer, $boundings ); + } + + // Draw basic odometer + $this->driver->options->font = $this->elements['axis']->font; + $boundings = $this->renderer->drawOdometer( + $boundings, + $this->elements['axis'], + $this->options + ); + + // Render graph + $this->renderData( $this->renderer, $boundings ); + } + + /** + * Render the pie chart + * + * Renders the chart into a file or stream. The width and height are + * needed to specify the dimensions of the resulting image. For direct + * output use 'php://stdout' as output file. + * + * @param int $width Image width + * @param int $height Image height + * @param string $file Output file + * @apichange + * @return void + */ + public function render( $width, $height, $file = null ) + { + $this->renderElements( $width, $height ); + + if ( !empty( $file ) ) + { + $this->renderer->render( $file ); + } + + $this->renderedFile = $file; + } + + /** + * Renders this chart to direct output + * + * Does the same as ezcGraphChart::render(), but renders directly to + * output and not into a file. + * + * @param int $width + * @param int $height + * @apichange + * @return void + */ + public function renderToOutput( $width, $height ) + { + // @TODO: merge this function with render an deprecate ommit of third + // argument in render() when API break is possible + $this->renderElements( $width, $height ); + $this->renderer->render( null ); + } +} + +?> diff --git a/library/ezc/Graph/src/charts/pie.php b/library/ezc/Graph/src/charts/pie.php index d8bbe10e1d..ac189ec556 100644 --- a/library/ezc/Graph/src/charts/pie.php +++ b/library/ezc/Graph/src/charts/pie.php @@ -1,308 +1,308 @@ - - * // Create a new pie chart - * $chart = new ezcGraphPieChart(); - * - * // Add data to line chart - * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( - * array( - * 'one' => 1.2, - * 'two' => 43.2, - * 'three' => -34.14, - * 'four' => 65, - * 'five' => 123, - * ) - * ); - * - * // Render chart with default 2d renderer and default SVG driver - * $chart->render( 500, 200, 'pie_chart.svg' ); - * - * - * Each chart consists of several chart elements which represents logical - * parts of the chart and can be formatted independently. The pie chart - * consists of: - * - title ( {@link ezcGraphChartElementText} ) - * - legend ( {@link ezcGraphChartElementLegend} ) - * - background ( {@link ezcGraphChartElementBackground} ) - * - * All elements can be configured by accessing them as properties of the chart: - * - * - * $chart->legend->position = ezcGraph::RIGHT; - * - * - * The chart itself also offers several options to configure the appearance. - * The extended configure options are available in - * {@link ezcGraphPieChartOptions} extending the {@link ezcGraphChartOptions}. - * - * @property ezcGraphPieChartOptions $options - * Chart options class - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphPieChart extends ezcGraphChart -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->options = new ezcGraphPieChartOptions( $options ); - - parent::__construct( $options ); - - $this->data = new ezcGraphChartSingleDataContainer( $this ); - } - - /** - * Render the assigned data - * - * Will renderer all charts data in the remaining boundings after drawing - * all other chart elements. The data will be rendered depending on the - * settings in the dataset. - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Remaining boundings - * @return void - */ - protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - // Only draw the first (and only) dataset - $dataset = $this->data->rewind(); - $datasetName = $this->data->key(); - - $this->driver->options->font = $this->options->font; - - // Calculate sum of all values to be able to calculate percentage - $sum = 0; - foreach ( $dataset as $name => $value ) - { - if ( $value < 0 ) - { - throw new ezcGraphInvalidDataException( "Values >= 0 required, '$name' => '$value'." ); - } - - $sum += $value; - } - if ( $this->options->sum !== false ) - { - $sum = max( $sum, $this->options->sum ); - } - - if ( $sum <= 0 ) - { - throw new ezcGraphInvalidDataException( "Pie charts require a value sum > 0, your value: '$sum'." ); - } - - $angle = 0; - foreach ( $dataset as $label => $value ) - { - // Skip rendering values which equals 0 - if ( $value <= 0 ) - { - continue; - } - - switch ( $dataset->displayType->default ) - { - case ezcGraph::PIE: - $displayLabel = ( $this->options->labelCallback !== null - ? call_user_func( $this->options->labelCallback, $label, $value, $value / $sum ) - : sprintf( $this->options->label, $label, $value, $value / $sum * 100 ) ); - - $renderer->drawPieSegment( - $boundings, - new ezcGraphContext( $datasetName, $label, $dataset->url[$label] ), - $dataset->color[$label], - $angle, - $angle += $value / $sum * 360, - $displayLabel, - $dataset->highlight[$label] - ); - break; - default: - throw new ezcGraphInvalidDisplayTypeException( $dataset->displayType->default ); - break; - } - } - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - public function getDefaultDisplayType() - { - return ezcGraph::PIE; - } - - /** - * Apply tresh hold - * - * Iterates over the dataset and applies the configured tresh hold to - * the datasets data. - * - * @return void - */ - protected function applyThreshold() - { - if ( $this->options->percentThreshold || $this->options->absoluteThreshold ) - { - $dataset = $this->data->rewind(); - - $sum = 0; - foreach ( $dataset as $value ) - { - $sum += $value; - } - if ( $this->options->sum !== false ) - { - $sum = max( $sum, $this->options->sum ); - } - - $unset = array(); - foreach ( $dataset as $label => $value ) - { - if ( $label === $this->options->summarizeCaption ) - { - continue; - } - - if ( ( $value <= $this->options->absoluteThreshold ) || - ( ( $value / $sum ) <= $this->options->percentThreshold ) ) - { - if ( !isset( $dataset[$this->options->summarizeCaption] ) ) - { - $dataset[$this->options->summarizeCaption] = $value; - } - else - { - $dataset[$this->options->summarizeCaption] += $value; - } - - $unset[] = $label; - } - } - - foreach ( $unset as $label ) - { - unset( $dataset[$label] ); - } - } - } - - /** - * Renders the basic elements of this chart type - * - * @param int $width - * @param int $height - * @return void - */ - protected function renderElements( $width, $height ) - { - if ( !count( $this->data ) ) - { - throw new ezcGraphNoDataException(); - } - - // Set image properties in driver - $this->driver->options->width = $width; - $this->driver->options->height = $height; - - // Apply tresh hold - $this->applyThreshold(); - - // Generate legend - $this->elements['legend']->generateFromDataSet( $this->data->rewind() ); - - // Get boundings from parameters - $this->options->width = $width; - $this->options->height = $height; - - $boundings = new ezcGraphBoundings(); - $boundings->x1 = $this->options->width; - $boundings->y1 = $this->options->height; - - // Render subelements - foreach ( $this->elements as $name => $element ) - { - // Skip element, if it should not get rendered - if ( $this->renderElement[$name] === false ) - { - continue; - } - - $this->driver->options->font = $element->font; - $boundings = $element->render( $this->renderer, $boundings ); - } - - // Render graph - $this->renderData( $this->renderer, $boundings ); - } - - /** - * Render the pie chart - * - * Renders the chart into a file or stream. The width and height are - * needed to specify the dimensions of the resulting image. For direct - * output use 'php://stdout' as output file. - * - * @param int $width Image width - * @param int $height Image height - * @param string $file Output file - * @apichange - * @return void - */ - public function render( $width, $height, $file = null ) - { - $this->renderElements( $width, $height ); - - if ( !empty( $file ) ) - { - $this->renderer->render( $file ); - } - - $this->renderedFile = $file; - } - - /** - * Renders this chart to direct output - * - * Does the same as ezcGraphChart::render(), but renders directly to - * output and not into a file. - * - * @param int $width - * @param int $height - * @apichange - * @return void - */ - public function renderToOutput( $width, $height ) - { - // @TODO: merge this function with render an deprecate ommit of third - // argument in render() when API break is possible - $this->renderElements( $width, $height ); - $this->renderer->render( null ); - } -} - -?> + + * // Create a new pie chart + * $chart = new ezcGraphPieChart(); + * + * // Add data to line chart + * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( + * array( + * 'one' => 1.2, + * 'two' => 43.2, + * 'three' => -34.14, + * 'four' => 65, + * 'five' => 123, + * ) + * ); + * + * // Render chart with default 2d renderer and default SVG driver + * $chart->render( 500, 200, 'pie_chart.svg' ); + * + * + * Each chart consists of several chart elements which represents logical + * parts of the chart and can be formatted independently. The pie chart + * consists of: + * - title ( {@link ezcGraphChartElementText} ) + * - legend ( {@link ezcGraphChartElementLegend} ) + * - background ( {@link ezcGraphChartElementBackground} ) + * + * All elements can be configured by accessing them as properties of the chart: + * + * + * $chart->legend->position = ezcGraph::RIGHT; + * + * + * The chart itself also offers several options to configure the appearance. + * The extended configure options are available in + * {@link ezcGraphPieChartOptions} extending the {@link ezcGraphChartOptions}. + * + * @property ezcGraphPieChartOptions $options + * Chart options class + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphPieChart extends ezcGraphChart +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->options = new ezcGraphPieChartOptions( $options ); + + parent::__construct( $options ); + + $this->data = new ezcGraphChartSingleDataContainer( $this ); + } + + /** + * Render the assigned data + * + * Will renderer all charts data in the remaining boundings after drawing + * all other chart elements. The data will be rendered depending on the + * settings in the dataset. + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Remaining boundings + * @return void + */ + protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + // Only draw the first (and only) dataset + $dataset = $this->data->rewind(); + $datasetName = $this->data->key(); + + $this->driver->options->font = $this->options->font; + + // Calculate sum of all values to be able to calculate percentage + $sum = 0; + foreach ( $dataset as $name => $value ) + { + if ( $value < 0 ) + { + throw new ezcGraphInvalidDataException( "Values >= 0 required, '$name' => '$value'." ); + } + + $sum += $value; + } + if ( $this->options->sum !== false ) + { + $sum = max( $sum, $this->options->sum ); + } + + if ( $sum <= 0 ) + { + throw new ezcGraphInvalidDataException( "Pie charts require a value sum > 0, your value: '$sum'." ); + } + + $angle = 0; + foreach ( $dataset as $label => $value ) + { + // Skip rendering values which equals 0 + if ( $value <= 0 ) + { + continue; + } + + switch ( $dataset->displayType->default ) + { + case ezcGraph::PIE: + $displayLabel = ( $this->options->labelCallback !== null + ? call_user_func( $this->options->labelCallback, $label, $value, $value / $sum ) + : sprintf( $this->options->label, $label, $value, $value / $sum * 100 ) ); + + $renderer->drawPieSegment( + $boundings, + new ezcGraphContext( $datasetName, $label, $dataset->url[$label] ), + $dataset->color[$label], + $angle, + $angle += $value / $sum * 360, + $displayLabel, + $dataset->highlight[$label] + ); + break; + default: + throw new ezcGraphInvalidDisplayTypeException( $dataset->displayType->default ); + break; + } + } + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + public function getDefaultDisplayType() + { + return ezcGraph::PIE; + } + + /** + * Apply tresh hold + * + * Iterates over the dataset and applies the configured tresh hold to + * the datasets data. + * + * @return void + */ + protected function applyThreshold() + { + if ( $this->options->percentThreshold || $this->options->absoluteThreshold ) + { + $dataset = $this->data->rewind(); + + $sum = 0; + foreach ( $dataset as $value ) + { + $sum += $value; + } + if ( $this->options->sum !== false ) + { + $sum = max( $sum, $this->options->sum ); + } + + $unset = array(); + foreach ( $dataset as $label => $value ) + { + if ( $label === $this->options->summarizeCaption ) + { + continue; + } + + if ( ( $value <= $this->options->absoluteThreshold ) || + ( ( $value / $sum ) <= $this->options->percentThreshold ) ) + { + if ( !isset( $dataset[$this->options->summarizeCaption] ) ) + { + $dataset[$this->options->summarizeCaption] = $value; + } + else + { + $dataset[$this->options->summarizeCaption] += $value; + } + + $unset[] = $label; + } + } + + foreach ( $unset as $label ) + { + unset( $dataset[$label] ); + } + } + } + + /** + * Renders the basic elements of this chart type + * + * @param int $width + * @param int $height + * @return void + */ + protected function renderElements( $width, $height ) + { + if ( !count( $this->data ) ) + { + throw new ezcGraphNoDataException(); + } + + // Set image properties in driver + $this->driver->options->width = $width; + $this->driver->options->height = $height; + + // Apply tresh hold + $this->applyThreshold(); + + // Generate legend + $this->elements['legend']->generateFromDataSet( $this->data->rewind() ); + + // Get boundings from parameters + $this->options->width = $width; + $this->options->height = $height; + + $boundings = new ezcGraphBoundings(); + $boundings->x1 = $this->options->width; + $boundings->y1 = $this->options->height; + + // Render subelements + foreach ( $this->elements as $name => $element ) + { + // Skip element, if it should not get rendered + if ( $this->renderElement[$name] === false ) + { + continue; + } + + $this->driver->options->font = $element->font; + $boundings = $element->render( $this->renderer, $boundings ); + } + + // Render graph + $this->renderData( $this->renderer, $boundings ); + } + + /** + * Render the pie chart + * + * Renders the chart into a file or stream. The width and height are + * needed to specify the dimensions of the resulting image. For direct + * output use 'php://stdout' as output file. + * + * @param int $width Image width + * @param int $height Image height + * @param string $file Output file + * @apichange + * @return void + */ + public function render( $width, $height, $file = null ) + { + $this->renderElements( $width, $height ); + + if ( !empty( $file ) ) + { + $this->renderer->render( $file ); + } + + $this->renderedFile = $file; + } + + /** + * Renders this chart to direct output + * + * Does the same as ezcGraphChart::render(), but renders directly to + * output and not into a file. + * + * @param int $width + * @param int $height + * @apichange + * @return void + */ + public function renderToOutput( $width, $height ) + { + // @TODO: merge this function with render an deprecate ommit of third + // argument in render() when API break is possible + $this->renderElements( $width, $height ); + $this->renderer->render( null ); + } +} + +?> diff --git a/library/ezc/Graph/src/charts/radar.php b/library/ezc/Graph/src/charts/radar.php index 2461513a0d..adc03788e7 100644 --- a/library/ezc/Graph/src/charts/radar.php +++ b/library/ezc/Graph/src/charts/radar.php @@ -1,457 +1,457 @@ - - * // Create a new radar chart - * $chart = new ezcGraphRadarChart(); - * - * // Add data to line chart - * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( - * array( - * '100' => 1.2, - * '200' => 43.2, - * '300' => -34.14, - * '350' => 65, - * '400' => 123, - * ) - * ); - * - * // Render chart with default 2d renderer and default SVG driver - * $chart->render( 500, 200, 'radar_chart.svg' ); - * - * - * Each chart consists of several chart elements which represents logical - * parts of the chart and can be formatted independently. The line chart - * consists of: - * - title ( {@link ezcGraphChartElementText} ) - * - legend ( {@link ezcGraphChartElementLegend} ) - * - background ( {@link ezcGraphChartElementBackground} ) - * - axis ( {@link ezcGraphChartElementNumericAxis} ) - * - ratation axis ( {@link ezcGraphChartElementLabeledAxis} ) - * - * The type of the axis may be changed and all elements can be configured by - * accessing them as properties of the chart: - * - * The chart itself also offers several options to configure the appearance. - * The extended configure options are available in - * {@link ezcGraphRadarChartOptions} extending the - * {@link ezcGraphChartOptions}. - * - * - * $chart->legend->position = ezcGraph::RIGHT; - * - * - * @property ezcGraphRadarChartOptions $options - * Chart options class - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphRadarChart extends ezcGraphChart -{ - /** - * Store major grid color for child axis. - * - * @var ezcGraphColor - */ - protected $childAxisColor; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->options = new ezcGraphRadarChartOptions( $options ); - $this->options->highlightFont = $this->options->font; - - parent::__construct(); - - $this->elements['rotationAxis'] = new ezcGraphChartElementLabeledAxis(); - - $this->addElement( 'axis', new ezcGraphChartElementNumericAxis() ); - $this->elements['axis']->position = ezcGraph::BOTTOM; - $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); - $this->elements['axis']->axisLabelRenderer->outerStep = true; - - $this->addElement( 'rotationAxis', new ezcGraphChartElementLabeledAxis() ); - - // Do not render axis with default method, because we need an axis for - // each label in dataset - $this->renderElement['axis'] = false; - $this->renderElement['rotationAxis'] = false; - } - - /** - * Set colors and border fro this element - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - $this->childAxisColor = $palette->majorGridColor; - - parent::setFromPalette( $palette ); - } - - /** - * Property write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param string $propertyName Option name - * @param mixed $propertyValue Option value; - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'axis': - if ( $propertyValue instanceof ezcGraphChartElementAxis ) - { - $this->addElement( 'axis', $propertyValue ); - $this->elements['axis']->position = ezcGraph::BOTTOM; - $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); - $this->renderElement['axis'] = false; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); - } - break; - case 'rotationAxis': - if ( $propertyValue instanceof ezcGraphChartElementAxis ) - { - $this->addElement( 'rotationAxis', $propertyValue ); - $this->renderElement['rotationAxis'] = false; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); - } - break; - case 'renderer': - if ( $propertyValue instanceof ezcGraphRadarRenderer ) - { - parent::__set( $propertyName, $propertyValue ); - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRadarRenderer' ); - } - break; - default: - parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Draws a single rotated axis - * - * Sets the axis label position depending on the axis rotation. - * - * @param ezcGraphChartElementAxis $axis - * @param ezcGraphBoundings $boundings - * @param ezcGraphCoordinate $center - * @param float $position - * @param float $lastPosition - * @return void - */ - protected function drawRotatedAxis( ezcGraphChartElementAxis $axis, ezcGraphBoundings $boundings, ezcGraphCoordinate $center, $position, $lastPosition = null ) - { - // Set axis position depending on angle for better axis label - // positioning - $angle = $position * 2 * M_PI; - switch ( (int) ( ( $position + .125 ) * 4 ) ) - { - case 0: - case 4: - $axis->position = ezcGraph::BOTTOM; - break; - case 1: - $axis->position = ezcGraph::LEFT; - break; - case 2: - $axis->position = ezcGraph::TOP; - break; - case 3: - $axis->position = ezcGraph::RIGHT; - break; - } - - // Set last step to correctly draw grid - if ( $axis->axisLabelRenderer instanceof ezcGraphAxisRadarLabelRenderer ) - { - $axis->axisLabelRenderer->lastStep = $lastPosition; - } - - // Do not draw axis label for last step - if ( abs( $position - 1 ) <= .001 ) - { - $axis->label = null; - } - - $this->renderer->drawAxis( - $boundings, - clone $center, - $dest = new ezcGraphCoordinate( - $center->x + sin( $angle ) * ( $boundings->width / 2 ), - $center->y - cos( $angle ) * ( $boundings->height / 2 ) - ), - clone $axis, - clone $axis->axisLabelRenderer - ); - } - - /** - * Render the assigned data - * - * Will renderer all charts data in the remaining boundings after drawing - * all other chart elements. The data will be rendered depending on the - * settings in the dataset. - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Remaining boundings - * @return void - */ - protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - // Apply axis space - $xAxisSpace = ( $boundings->x1 - $boundings->x0 ) * $this->axis->axisSpace; - $yAxisSpace = ( $boundings->y1 - $boundings->y0 ) * $this->axis->axisSpace; - - $center = new ezcGraphCoordinate( - ( $boundings->width / 2 ), - ( $boundings->height / 2 ) - ); - - // We do not differentiate between display types in radar charts. - $nr = $count = count( $this->data ); - - // Draw axis at major steps of virtual axis - $steps = $this->elements['rotationAxis']->getSteps(); - $lastStepPosition = null; - $axisColor = $this->elements['axis']->border; - foreach ( $steps as $step ) - { - $this->elements['axis']->label = $step->label; - $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $step->position, $lastStepPosition ); - $lastStepPosition = $step->position; - - if ( count( $step->childs ) ) - { - foreach ( $step->childs as $childStep ) - { - $this->elements['axis']->label = null; - $this->elements['axis']->border = $this->childAxisColor; - - $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $childStep->position, $lastStepPosition ); - $lastStepPosition = $childStep->position; - } - } - - $this->elements['axis']->border = $axisColor; - } - - // Display data - $this->elements['axis']->position = ezcGraph::TOP; - foreach ( $this->data as $datasetName => $data ) - { - --$nr; - // Determine fill color for dataset - if ( $this->options->fillLines !== false ) - { - $fillColor = clone $data->color->default; - $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) ); - } - else - { - $fillColor = null; - } - - // Draw lines for dataset - $lastPoint = false; - foreach ( $data as $key => $value ) - { - $point = new ezcGraphCoordinate( - $this->elements['rotationAxis']->getCoordinate( $key ), - $this->elements['axis']->getCoordinate( $value ) - ); - - /* Transformation required for 3d like renderers ... - * which axis should transform here? - $point = $this->elements['xAxis']->axisLabelRenderer->modifyChartDataPosition( - $this->elements['yAxis']->axisLabelRenderer->modifyChartDataPosition( - new ezcGraphCoordinate( - $this->elements['xAxis']->getCoordinate( $key ), - $this->elements['yAxis']->getCoordinate( $value ) - ) - ) - ); - // */ - - $renderer->drawRadarDataLine( - $boundings, - new ezcGraphContext( $datasetName, $key, $data->url[$key] ), - $data->color->default, - clone $center, - ( $lastPoint === false ? $point : $lastPoint ), - $point, - $nr, - $count, - $data->symbol[$key], - $data->color[$key], - $fillColor, - $this->options->lineThickness - ); - - $lastPoint = $point; - } - } - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - public function getDefaultDisplayType() - { - return ezcGraph::LINE; - } - - /** - * Renders the basic elements of this chart type - * - * @param int $width - * @param int $height - * @return void - */ - protected function renderElements( $width, $height ) - { - if ( !count( $this->data ) ) - { - throw new ezcGraphNoDataException(); - } - - // Set image properties in driver - $this->driver->options->width = $width; - $this->driver->options->height = $height; - - // Calculate axis scaling and labeling - foreach ( $this->data as $dataset ) - { - $labels = array(); - $values = array(); - foreach ( $dataset as $label => $value ) - { - $labels[] = $label; - $values[] = $value; - } - - $this->elements['axis']->addData( $values ); - $this->elements['rotationAxis']->addData( $labels ); - } - - $this->elements['axis']->calculateAxisBoundings(); - $this->elements['rotationAxis']->calculateAxisBoundings(); - - // Generate legend - $this->elements['legend']->generateFromDataSets( $this->data ); - - // Get boundings from parameters - $this->options->width = $width; - $this->options->height = $height; - - // Render subelements - $boundings = new ezcGraphBoundings(); - $boundings->x1 = $this->options->width; - $boundings->y1 = $this->options->height; - - // Render subelements - foreach ( $this->elements as $name => $element ) - { - // Skip element, if it should not get rendered - if ( $this->renderElement[$name] === false ) - { - continue; - } - - $this->driver->options->font = $element->font; - $boundings = $element->render( $this->renderer, $boundings ); - } - - // Render graph - $this->renderData( $this->renderer, $boundings ); - } - - /** - * Render the line chart - * - * Renders the chart into a file or stream. The width and height are - * needed to specify the dimensions of the resulting image. For direct - * output use 'php://stdout' as output file. - * - * @param int $width Image width - * @param int $height Image height - * @param string $file Output file - * @apichange - * @return void - */ - public function render( $width, $height, $file = null ) - { - $this->renderElements( $width, $height ); - - if ( !empty( $file ) ) - { - $this->renderer->render( $file ); - } - - $this->renderedFile = $file; - } - - /** - * Renders this chart to direct output - * - * Does the same as ezcGraphChart::render(), but renders directly to - * output and not into a file. - * - * @param int $width - * @param int $height - * @apichange - * @return void - */ - public function renderToOutput( $width, $height ) - { - // @TODO: merge this function with render an deprecate ommit of third - // argument in render() when API break is possible - $this->renderElements( $width, $height ); - $this->renderer->render( null ); - } -} -?> + + * // Create a new radar chart + * $chart = new ezcGraphRadarChart(); + * + * // Add data to line chart + * $chart->data['sample dataset'] = new ezcGraphArrayDataSet( + * array( + * '100' => 1.2, + * '200' => 43.2, + * '300' => -34.14, + * '350' => 65, + * '400' => 123, + * ) + * ); + * + * // Render chart with default 2d renderer and default SVG driver + * $chart->render( 500, 200, 'radar_chart.svg' ); + * + * + * Each chart consists of several chart elements which represents logical + * parts of the chart and can be formatted independently. The line chart + * consists of: + * - title ( {@link ezcGraphChartElementText} ) + * - legend ( {@link ezcGraphChartElementLegend} ) + * - background ( {@link ezcGraphChartElementBackground} ) + * - axis ( {@link ezcGraphChartElementNumericAxis} ) + * - ratation axis ( {@link ezcGraphChartElementLabeledAxis} ) + * + * The type of the axis may be changed and all elements can be configured by + * accessing them as properties of the chart: + * + * The chart itself also offers several options to configure the appearance. + * The extended configure options are available in + * {@link ezcGraphRadarChartOptions} extending the + * {@link ezcGraphChartOptions}. + * + * + * $chart->legend->position = ezcGraph::RIGHT; + * + * + * @property ezcGraphRadarChartOptions $options + * Chart options class + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphRadarChart extends ezcGraphChart +{ + /** + * Store major grid color for child axis. + * + * @var ezcGraphColor + */ + protected $childAxisColor; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->options = new ezcGraphRadarChartOptions( $options ); + $this->options->highlightFont = $this->options->font; + + parent::__construct(); + + $this->elements['rotationAxis'] = new ezcGraphChartElementLabeledAxis(); + + $this->addElement( 'axis', new ezcGraphChartElementNumericAxis() ); + $this->elements['axis']->position = ezcGraph::BOTTOM; + $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); + $this->elements['axis']->axisLabelRenderer->outerStep = true; + + $this->addElement( 'rotationAxis', new ezcGraphChartElementLabeledAxis() ); + + // Do not render axis with default method, because we need an axis for + // each label in dataset + $this->renderElement['axis'] = false; + $this->renderElement['rotationAxis'] = false; + } + + /** + * Set colors and border fro this element + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + $this->childAxisColor = $palette->majorGridColor; + + parent::setFromPalette( $palette ); + } + + /** + * Property write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param string $propertyName Option name + * @param mixed $propertyValue Option value; + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'axis': + if ( $propertyValue instanceof ezcGraphChartElementAxis ) + { + $this->addElement( 'axis', $propertyValue ); + $this->elements['axis']->position = ezcGraph::BOTTOM; + $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); + $this->renderElement['axis'] = false; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); + } + break; + case 'rotationAxis': + if ( $propertyValue instanceof ezcGraphChartElementAxis ) + { + $this->addElement( 'rotationAxis', $propertyValue ); + $this->renderElement['rotationAxis'] = false; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' ); + } + break; + case 'renderer': + if ( $propertyValue instanceof ezcGraphRadarRenderer ) + { + parent::__set( $propertyName, $propertyValue ); + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRadarRenderer' ); + } + break; + default: + parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Draws a single rotated axis + * + * Sets the axis label position depending on the axis rotation. + * + * @param ezcGraphChartElementAxis $axis + * @param ezcGraphBoundings $boundings + * @param ezcGraphCoordinate $center + * @param float $position + * @param float $lastPosition + * @return void + */ + protected function drawRotatedAxis( ezcGraphChartElementAxis $axis, ezcGraphBoundings $boundings, ezcGraphCoordinate $center, $position, $lastPosition = null ) + { + // Set axis position depending on angle for better axis label + // positioning + $angle = $position * 2 * M_PI; + switch ( (int) ( ( $position + .125 ) * 4 ) ) + { + case 0: + case 4: + $axis->position = ezcGraph::BOTTOM; + break; + case 1: + $axis->position = ezcGraph::LEFT; + break; + case 2: + $axis->position = ezcGraph::TOP; + break; + case 3: + $axis->position = ezcGraph::RIGHT; + break; + } + + // Set last step to correctly draw grid + if ( $axis->axisLabelRenderer instanceof ezcGraphAxisRadarLabelRenderer ) + { + $axis->axisLabelRenderer->lastStep = $lastPosition; + } + + // Do not draw axis label for last step + if ( abs( $position - 1 ) <= .001 ) + { + $axis->label = null; + } + + $this->renderer->drawAxis( + $boundings, + clone $center, + $dest = new ezcGraphCoordinate( + $center->x + sin( $angle ) * ( $boundings->width / 2 ), + $center->y - cos( $angle ) * ( $boundings->height / 2 ) + ), + clone $axis, + clone $axis->axisLabelRenderer + ); + } + + /** + * Render the assigned data + * + * Will renderer all charts data in the remaining boundings after drawing + * all other chart elements. The data will be rendered depending on the + * settings in the dataset. + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Remaining boundings + * @return void + */ + protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + // Apply axis space + $xAxisSpace = ( $boundings->x1 - $boundings->x0 ) * $this->axis->axisSpace; + $yAxisSpace = ( $boundings->y1 - $boundings->y0 ) * $this->axis->axisSpace; + + $center = new ezcGraphCoordinate( + ( $boundings->width / 2 ), + ( $boundings->height / 2 ) + ); + + // We do not differentiate between display types in radar charts. + $nr = $count = count( $this->data ); + + // Draw axis at major steps of virtual axis + $steps = $this->elements['rotationAxis']->getSteps(); + $lastStepPosition = null; + $axisColor = $this->elements['axis']->border; + foreach ( $steps as $step ) + { + $this->elements['axis']->label = $step->label; + $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $step->position, $lastStepPosition ); + $lastStepPosition = $step->position; + + if ( count( $step->childs ) ) + { + foreach ( $step->childs as $childStep ) + { + $this->elements['axis']->label = null; + $this->elements['axis']->border = $this->childAxisColor; + + $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $childStep->position, $lastStepPosition ); + $lastStepPosition = $childStep->position; + } + } + + $this->elements['axis']->border = $axisColor; + } + + // Display data + $this->elements['axis']->position = ezcGraph::TOP; + foreach ( $this->data as $datasetName => $data ) + { + --$nr; + // Determine fill color for dataset + if ( $this->options->fillLines !== false ) + { + $fillColor = clone $data->color->default; + $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) ); + } + else + { + $fillColor = null; + } + + // Draw lines for dataset + $lastPoint = false; + foreach ( $data as $key => $value ) + { + $point = new ezcGraphCoordinate( + $this->elements['rotationAxis']->getCoordinate( $key ), + $this->elements['axis']->getCoordinate( $value ) + ); + + /* Transformation required for 3d like renderers ... + * which axis should transform here? + $point = $this->elements['xAxis']->axisLabelRenderer->modifyChartDataPosition( + $this->elements['yAxis']->axisLabelRenderer->modifyChartDataPosition( + new ezcGraphCoordinate( + $this->elements['xAxis']->getCoordinate( $key ), + $this->elements['yAxis']->getCoordinate( $value ) + ) + ) + ); + // */ + + $renderer->drawRadarDataLine( + $boundings, + new ezcGraphContext( $datasetName, $key, $data->url[$key] ), + $data->color->default, + clone $center, + ( $lastPoint === false ? $point : $lastPoint ), + $point, + $nr, + $count, + $data->symbol[$key], + $data->color[$key], + $fillColor, + $this->options->lineThickness + ); + + $lastPoint = $point; + } + } + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + public function getDefaultDisplayType() + { + return ezcGraph::LINE; + } + + /** + * Renders the basic elements of this chart type + * + * @param int $width + * @param int $height + * @return void + */ + protected function renderElements( $width, $height ) + { + if ( !count( $this->data ) ) + { + throw new ezcGraphNoDataException(); + } + + // Set image properties in driver + $this->driver->options->width = $width; + $this->driver->options->height = $height; + + // Calculate axis scaling and labeling + foreach ( $this->data as $dataset ) + { + $labels = array(); + $values = array(); + foreach ( $dataset as $label => $value ) + { + $labels[] = $label; + $values[] = $value; + } + + $this->elements['axis']->addData( $values ); + $this->elements['rotationAxis']->addData( $labels ); + } + + $this->elements['axis']->calculateAxisBoundings(); + $this->elements['rotationAxis']->calculateAxisBoundings(); + + // Generate legend + $this->elements['legend']->generateFromDataSets( $this->data ); + + // Get boundings from parameters + $this->options->width = $width; + $this->options->height = $height; + + // Render subelements + $boundings = new ezcGraphBoundings(); + $boundings->x1 = $this->options->width; + $boundings->y1 = $this->options->height; + + // Render subelements + foreach ( $this->elements as $name => $element ) + { + // Skip element, if it should not get rendered + if ( $this->renderElement[$name] === false ) + { + continue; + } + + $this->driver->options->font = $element->font; + $boundings = $element->render( $this->renderer, $boundings ); + } + + // Render graph + $this->renderData( $this->renderer, $boundings ); + } + + /** + * Render the line chart + * + * Renders the chart into a file or stream. The width and height are + * needed to specify the dimensions of the resulting image. For direct + * output use 'php://stdout' as output file. + * + * @param int $width Image width + * @param int $height Image height + * @param string $file Output file + * @apichange + * @return void + */ + public function render( $width, $height, $file = null ) + { + $this->renderElements( $width, $height ); + + if ( !empty( $file ) ) + { + $this->renderer->render( $file ); + } + + $this->renderedFile = $file; + } + + /** + * Renders this chart to direct output + * + * Does the same as ezcGraphChart::render(), but renders directly to + * output and not into a file. + * + * @param int $width + * @param int $height + * @apichange + * @return void + */ + public function renderToOutput( $width, $height ) + { + // @TODO: merge this function with render an deprecate ommit of third + // argument in render() when API break is possible + $this->renderElements( $width, $height ); + $this->renderer->render( null ); + } +} +?> diff --git a/library/ezc/Graph/src/colors/color.php b/library/ezc/Graph/src/colors/color.php index 7a989493eb..d310cca443 100644 --- a/library/ezc/Graph/src/colors/color.php +++ b/library/ezc/Graph/src/colors/color.php @@ -1,290 +1,290 @@ -properties['red'] = 0; - $this->properties['green'] = 0; - $this->properties['blue'] = 0; - $this->properties['alpha'] = 0; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'red': - case 'green': - case 'blue': - case 'alpha': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 255 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 255' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * Creates an ezcGraphColor object from a hexadecimal color representation - * - * @param mixed $string Hexadecimal color representation - * @return ezcGraphColor - */ - static public function fromHex( $string ) - { - // Remove trailing # - if ( $string[0] === '#' ) - { - $string = substr( $string, 1 ); - } - - // Iterate over chunks and convert to integer - $color = new ezcGraphColor(); - $keys = array( 'red', 'green', 'blue', 'alpha' ); - foreach ( str_split( $string, 2) as $nr => $hexValue ) - { - if ( isset( $keys[$nr] ) ) - { - $key = $keys[$nr]; - $color->$key = hexdec( $hexValue ) % 256; - } - } - - // Set missing values to zero - for ( ++$nr; $nr < count( $keys ); ++$nr ) - { - $key = $keys[$nr]; - $color->$key = 0; - } - - return $color; - } - - /** - * Creates an ezcGraphColor object from an array of integers - * - * @param array $array Array of integer color values - * @return ezcGraphColor - */ - static public function fromIntegerArray( array $array ) - { - // Iterate over array elements - $color = new ezcGraphColor(); - $keys = array( 'red', 'green', 'blue', 'alpha' ); - $nr = 0; - foreach ( $array as $colorValue ) - { - if ( isset( $keys[$nr] ) ) - { - $key = $keys[$nr++]; - $color->$key = ( (int) $colorValue ) % 256; - } - } - - // Set missing values to zero - for ( $nr; $nr < count( $keys ); ++$nr ) - { - $key = $keys[$nr]; - $color->$key = 0; - } - - return $color; - } - - /** - * Creates an ezcGraphColor object from an array of floats - * - * @param array $array Array of float color values - * @return ezcGraphColor - */ - static public function fromFloatArray( array $array ) - { - // Iterate over array elements - $color = new ezcGraphColor(); - $keys = array( 'red', 'green', 'blue', 'alpha' ); - $nr = 0; - foreach ( $array as $colorValue ) - { - if ( isset( $keys[$nr] ) ) - { - $key = $keys[$nr++]; - $color->$key = ( (float) $colorValue * 255 ) % 256; - } - } - - // Set missing values to zero - for ( $nr; $nr < count( $keys ); ++$nr ) - { - $key = $keys[$nr]; - $color->$key = 0; - } - - return $color; - } - - /** - * Tries to parse provided color value - * - * This method can be used to create a color struct from arbritrary color - * representations. The following values are accepted - * - * - Hexadecimal color definitions, like known from HTML, CSS and SVG - * - * Color definitions like #FF0000, with and and without number sign, - * where each pair of bytes is interpreted as a color value for the - * channels RGB(A). These color values may contain up to 4 values, where - * the last value is considered as the alpha channel. - * - * - Array of integers - * - * If an array of integers is provided as input teh value in each channel - * may be in the span [0 - 255] and is assigned to the color channels - * RGB(A). Up to four values are used from the array. - * - * - Array of floats - * - * If an array of floats is provided as input teh value in each channel - * may be in the span [0 - 1] and is assigned to the color channels - * RGB(A). Up to four values are used from the array. - * - * @param mixed $color Some kind of color definition - * @return ezcGraphColor - */ - static public function create( $color ) - { - if ( $color instanceof ezcGraphColor ) - { - return $color; - } - elseif ( is_string( $color ) ) - { - return ezcGraphColor::fromHex( $color ); - } - elseif ( is_array( $color ) ) - { - $testElement = reset( $color ); - if ( is_int( $testElement ) ) - { - return ezcGraphColor::fromIntegerArray( $color ); - } - else - { - return ezcGraphColor::fromFloatArray( $color ); - } - } - else - { - throw new ezcGraphUnknownColorDefinitionException( $color ); - } - } - - /** - * Returns a copy of the current color made more transparent by the given - * factor - * - * @param mixed $value Percent to make color mor transparent - * @return ezcGraphColor New color - */ - public function transparent( $value ) - { - $color = clone $this; - - $color->alpha = 255 - (int) round( ( 255 - $this->alpha ) * ( 1 - $value ) ); - - return $color; - } - - /** - * Inverts and returns a copy of the current color - * - * @return ezcGraphColor New Color - */ - public function invert() - { - $color = new ezcGraphColor(); - - $color->red = 255 - $this->red; - $color->green = 255 - $this->green; - $color->blue = 255 - $this->blue; - $color->alpha = $this->alpha; - - return $color; - } - - /** - * Returns a copy of the current color darkened by the given factor - * - * @param float $value Percent to darken the color - * @return ezcGraphColor New color - */ - public function darken( $value ) - { - $color = clone $this; - - $value = 1 - $value; - $color->red = min( 255, max( 0, (int) round( $this->red * $value ) ) ); - $color->green = min( 255, max( 0, (int) round( $this->green * $value ) ) ); - $color->blue = min( 255, max( 0, (int) round( $this->blue * $value ) ) ); - - return $color; - } -} - -?> +properties['red'] = 0; + $this->properties['green'] = 0; + $this->properties['blue'] = 0; + $this->properties['alpha'] = 0; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'red': + case 'green': + case 'blue': + case 'alpha': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 255 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 255' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * Creates an ezcGraphColor object from a hexadecimal color representation + * + * @param mixed $string Hexadecimal color representation + * @return ezcGraphColor + */ + static public function fromHex( $string ) + { + // Remove trailing # + if ( $string[0] === '#' ) + { + $string = substr( $string, 1 ); + } + + // Iterate over chunks and convert to integer + $color = new ezcGraphColor(); + $keys = array( 'red', 'green', 'blue', 'alpha' ); + foreach ( str_split( $string, 2) as $nr => $hexValue ) + { + if ( isset( $keys[$nr] ) ) + { + $key = $keys[$nr]; + $color->$key = hexdec( $hexValue ) % 256; + } + } + + // Set missing values to zero + for ( ++$nr; $nr < count( $keys ); ++$nr ) + { + $key = $keys[$nr]; + $color->$key = 0; + } + + return $color; + } + + /** + * Creates an ezcGraphColor object from an array of integers + * + * @param array $array Array of integer color values + * @return ezcGraphColor + */ + static public function fromIntegerArray( array $array ) + { + // Iterate over array elements + $color = new ezcGraphColor(); + $keys = array( 'red', 'green', 'blue', 'alpha' ); + $nr = 0; + foreach ( $array as $colorValue ) + { + if ( isset( $keys[$nr] ) ) + { + $key = $keys[$nr++]; + $color->$key = ( (int) $colorValue ) % 256; + } + } + + // Set missing values to zero + for ( $nr; $nr < count( $keys ); ++$nr ) + { + $key = $keys[$nr]; + $color->$key = 0; + } + + return $color; + } + + /** + * Creates an ezcGraphColor object from an array of floats + * + * @param array $array Array of float color values + * @return ezcGraphColor + */ + static public function fromFloatArray( array $array ) + { + // Iterate over array elements + $color = new ezcGraphColor(); + $keys = array( 'red', 'green', 'blue', 'alpha' ); + $nr = 0; + foreach ( $array as $colorValue ) + { + if ( isset( $keys[$nr] ) ) + { + $key = $keys[$nr++]; + $color->$key = ( (float) $colorValue * 255 ) % 256; + } + } + + // Set missing values to zero + for ( $nr; $nr < count( $keys ); ++$nr ) + { + $key = $keys[$nr]; + $color->$key = 0; + } + + return $color; + } + + /** + * Tries to parse provided color value + * + * This method can be used to create a color struct from arbritrary color + * representations. The following values are accepted + * + * - Hexadecimal color definitions, like known from HTML, CSS and SVG + * + * Color definitions like #FF0000, with and and without number sign, + * where each pair of bytes is interpreted as a color value for the + * channels RGB(A). These color values may contain up to 4 values, where + * the last value is considered as the alpha channel. + * + * - Array of integers + * + * If an array of integers is provided as input teh value in each channel + * may be in the span [0 - 255] and is assigned to the color channels + * RGB(A). Up to four values are used from the array. + * + * - Array of floats + * + * If an array of floats is provided as input teh value in each channel + * may be in the span [0 - 1] and is assigned to the color channels + * RGB(A). Up to four values are used from the array. + * + * @param mixed $color Some kind of color definition + * @return ezcGraphColor + */ + static public function create( $color ) + { + if ( $color instanceof ezcGraphColor ) + { + return $color; + } + elseif ( is_string( $color ) ) + { + return ezcGraphColor::fromHex( $color ); + } + elseif ( is_array( $color ) ) + { + $testElement = reset( $color ); + if ( is_int( $testElement ) ) + { + return ezcGraphColor::fromIntegerArray( $color ); + } + else + { + return ezcGraphColor::fromFloatArray( $color ); + } + } + else + { + throw new ezcGraphUnknownColorDefinitionException( $color ); + } + } + + /** + * Returns a copy of the current color made more transparent by the given + * factor + * + * @param mixed $value Percent to make color mor transparent + * @return ezcGraphColor New color + */ + public function transparent( $value ) + { + $color = clone $this; + + $color->alpha = 255 - (int) round( ( 255 - $this->alpha ) * ( 1 - $value ) ); + + return $color; + } + + /** + * Inverts and returns a copy of the current color + * + * @return ezcGraphColor New Color + */ + public function invert() + { + $color = new ezcGraphColor(); + + $color->red = 255 - $this->red; + $color->green = 255 - $this->green; + $color->blue = 255 - $this->blue; + $color->alpha = $this->alpha; + + return $color; + } + + /** + * Returns a copy of the current color darkened by the given factor + * + * @param float $value Percent to darken the color + * @return ezcGraphColor New color + */ + public function darken( $value ) + { + $color = clone $this; + + $value = 1 - $value; + $color->red = min( 255, max( 0, (int) round( $this->red * $value ) ) ); + $color->green = min( 255, max( 0, (int) round( $this->green * $value ) ) ); + $color->blue = min( 255, max( 0, (int) round( $this->blue * $value ) ) ); + + return $color; + } +} + +?> diff --git a/library/ezc/Graph/src/colors/linear_gradient.php b/library/ezc/Graph/src/colors/linear_gradient.php index 926da2a89d..5b51797369 100644 --- a/library/ezc/Graph/src/colors/linear_gradient.php +++ b/library/ezc/Graph/src/colors/linear_gradient.php @@ -1,147 +1,147 @@ -properties['startColor'] = $startColor; - $this->properties['endColor'] = $endColor; - $this->properties['startPoint'] = $startPoint; - $this->properties['endPoint'] = $endPoint; - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'startPoint': - if ( !$propertyValue instanceof ezcGraphCoordinate ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); - } - else - { - $this->properties['startPoint'] = $propertyValue; - } - break; - case 'endPoint': - if ( !$propertyValue instanceof ezcGraphCoordinate ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); - } - else - { - $this->properties['endPoint'] = $propertyValue; - } - break; - case 'startColor': - $this->properties['startColor'] = ezcGraphColor::create( $propertyValue ); - break; - case 'endColor': - $this->properties['endColor'] = ezcGraphColor::create( $propertyValue ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'red': - case 'green': - case 'blue': - case 'alpha': - // Fallback to native color - return $this->properties['startColor']->$propertyName; - default: - if ( isset( $this->properties[$propertyName] ) ) - { - return $this->properties[$propertyName]; - } - else - { - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - } - - /** - * Returns a unique string representation for the gradient. - * - * @access public - * @return void - */ - public function __toString() - { - return sprintf( 'LinearGradient_%d_%d_%d_%d_%02x%02x%02x%02x_%02x%02x%02x%02x', - $this->properties['startPoint']->x, - $this->properties['startPoint']->y, - $this->properties['endPoint']->x, - $this->properties['endPoint']->y, - $this->properties['startColor']->red, - $this->properties['startColor']->green, - $this->properties['startColor']->blue, - $this->properties['startColor']->alpha, - $this->properties['endColor']->red, - $this->properties['endColor']->green, - $this->properties['endColor']->blue, - $this->properties['endColor']->alpha - ); - } -} -?> +properties['startColor'] = $startColor; + $this->properties['endColor'] = $endColor; + $this->properties['startPoint'] = $startPoint; + $this->properties['endPoint'] = $endPoint; + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'startPoint': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['startPoint'] = $propertyValue; + } + break; + case 'endPoint': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['endPoint'] = $propertyValue; + } + break; + case 'startColor': + $this->properties['startColor'] = ezcGraphColor::create( $propertyValue ); + break; + case 'endColor': + $this->properties['endColor'] = ezcGraphColor::create( $propertyValue ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'red': + case 'green': + case 'blue': + case 'alpha': + // Fallback to native color + return $this->properties['startColor']->$propertyName; + default: + if ( isset( $this->properties[$propertyName] ) ) + { + return $this->properties[$propertyName]; + } + else + { + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + } + + /** + * Returns a unique string representation for the gradient. + * + * @access public + * @return void + */ + public function __toString() + { + return sprintf( 'LinearGradient_%d_%d_%d_%d_%02x%02x%02x%02x_%02x%02x%02x%02x', + $this->properties['startPoint']->x, + $this->properties['startPoint']->y, + $this->properties['endPoint']->x, + $this->properties['endPoint']->y, + $this->properties['startColor']->red, + $this->properties['startColor']->green, + $this->properties['startColor']->blue, + $this->properties['startColor']->alpha, + $this->properties['endColor']->red, + $this->properties['endColor']->green, + $this->properties['endColor']->blue, + $this->properties['endColor']->alpha + ); + } +} +?> diff --git a/library/ezc/Graph/src/colors/radial_gradient.php b/library/ezc/Graph/src/colors/radial_gradient.php index 557914d9f0..2d046cb9b2 100644 --- a/library/ezc/Graph/src/colors/radial_gradient.php +++ b/library/ezc/Graph/src/colors/radial_gradient.php @@ -1,173 +1,173 @@ -properties['center'] = $center; - $this->properties['width'] = (float) $width; - $this->properties['height'] = (float) $height; - $this->properties['offset'] = 0; - $this->properties['startColor'] = $startColor; - $this->properties['endColor'] = $endColor; - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'center': - if ( !$propertyValue instanceof ezcGraphCoordinate ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); - } - else - { - $this->properties['center'] = $propertyValue; - } - break; - case 'width': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['width'] = (float) $propertyValue; - break; - case 'height': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['height'] = (float) $propertyValue; - break; - case 'offset': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['offset'] = $propertyValue; - break; - case 'startColor': - $this->properties['startColor'] = ezcGraphColor::create( $propertyValue ); - break; - case 'endColor': - $this->properties['endColor'] = ezcGraphColor::create( $propertyValue ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'red': - case 'green': - case 'blue': - case 'alpha': - // Fallback to native color - return $this->properties['startColor']->$propertyName; - default: - if ( isset( $this->properties[$propertyName] ) ) - { - return $this->properties[$propertyName]; - } - else - { - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - } - - /** - * Returns a unique string representation for the gradient. - * - * @access public - * @return void - */ - public function __toString() - { - return sprintf( 'RadialGradient_%d_%d_%d_%d_%.2f_%02x%02x%02x%02x_%02x%02x%02x%02x', - $this->properties['center']->x, - $this->properties['center']->y, - $this->properties['width'], - $this->properties['height'], - $this->properties['offset'], - $this->properties['startColor']->red, - $this->properties['startColor']->green, - $this->properties['startColor']->blue, - $this->properties['startColor']->alpha, - $this->properties['endColor']->red, - $this->properties['endColor']->green, - $this->properties['endColor']->blue, - $this->properties['endColor']->alpha - ); - } -} -?> +properties['center'] = $center; + $this->properties['width'] = (float) $width; + $this->properties['height'] = (float) $height; + $this->properties['offset'] = 0; + $this->properties['startColor'] = $startColor; + $this->properties['endColor'] = $endColor; + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'center': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['center'] = $propertyValue; + } + break; + case 'width': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['width'] = (float) $propertyValue; + break; + case 'height': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['height'] = (float) $propertyValue; + break; + case 'offset': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['offset'] = $propertyValue; + break; + case 'startColor': + $this->properties['startColor'] = ezcGraphColor::create( $propertyValue ); + break; + case 'endColor': + $this->properties['endColor'] = ezcGraphColor::create( $propertyValue ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'red': + case 'green': + case 'blue': + case 'alpha': + // Fallback to native color + return $this->properties['startColor']->$propertyName; + default: + if ( isset( $this->properties[$propertyName] ) ) + { + return $this->properties[$propertyName]; + } + else + { + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + } + + /** + * Returns a unique string representation for the gradient. + * + * @access public + * @return void + */ + public function __toString() + { + return sprintf( 'RadialGradient_%d_%d_%d_%d_%.2f_%02x%02x%02x%02x_%02x%02x%02x%02x', + $this->properties['center']->x, + $this->properties['center']->y, + $this->properties['width'], + $this->properties['height'], + $this->properties['offset'], + $this->properties['startColor']->red, + $this->properties['startColor']->green, + $this->properties['startColor']->blue, + $this->properties['startColor']->alpha, + $this->properties['endColor']->red, + $this->properties['endColor']->green, + $this->properties['endColor']->blue, + $this->properties['endColor']->alpha + ); + } +} +?> diff --git a/library/ezc/Graph/src/data_container/base.php b/library/ezc/Graph/src/data_container/base.php index e394765b2f..dfe87b119c 100644 --- a/library/ezc/Graph/src/data_container/base.php +++ b/library/ezc/Graph/src/data_container/base.php @@ -1,225 +1,225 @@ -chart = $chart; - } - - /** - * Adds a dataset to the charts data - * - * @param string $name Name of dataset - * @param ezcGraphDataSet $dataSet - * @param mixed $values Values to create dataset with - * @throws ezcGraphTooManyDataSetExceptions - * If too many datasets are created - * @return ezcGraphDataSet - */ - protected function addDataSet( $name, ezcGraphDataSet $dataSet ) - { - $this->data[$name] = $dataSet; - - $this->data[$name]->label = $name; - $this->data[$name]->palette = $this->chart->palette; - $this->data[$name]->displayType = $this->chart->getDefaultDisplayType(); - } - - /** - * Returns if the given offset exists. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key Identifier of dataset. - * @return bool True when the offset exists, otherwise false. - */ - public function offsetExists( $key ) - { - return isset( $this->data[$key] ); - } - - /** - * Returns the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key Identifier of dataset. - * @return ezcGraphDataSet - * - * @throws ezcGraphNoSuchDataSetException - * If no dataset with identifier exists - */ - public function offsetGet( $key ) - { - if ( !isset( $this->data[$key] ) ) - { - throw new ezcGraphNoSuchDataSetException( $key ); - } - - return $this->data[$key]; - } - - /** - * Set the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key - * @param ezcGraphDataSet $value - * @return void - * - * @throws ezcBaseValueException - * If supplied value is not an ezcGraphDataSet - */ - public function offsetSet( $key, $value ) - { - if ( !$value instanceof ezcGraphDataSet ) - { - throw new ezcBaseValueException( $key, $value, 'ezcGraphDataSet' ); - } - - return $this->addDataSet( $key, $value ); - } - - /** - * Unset the element with the given offset. - * - * This method is part of the ArrayAccess interface to allow access to the - * data of this object as if it was an array. - * - * @param string $key - * @return void - */ - public function offsetUnset( $key ) - { - if ( !isset( $this->data[$key] ) ) - { - throw new ezcGraphNoSuchDataSetException( $key ); - } - - unset( $this->data[$key] ); - } - - /** - * Returns the currently selected dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return ezcGraphDataSet The currently selected dataset. - */ - public function current() - { - return current( $this->data ); - } - - /** - * Returns the next dataset and selects it or false on the last dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return mixed ezcGraphDataSet if the next dataset exists, or false. - */ - public function next() - { - return next( $this->data ); - } - - /** - * Returns the key of the currently selected dataset. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return int The key of the currently selected dataset. - */ - public function key() - { - return key( $this->data ); - } - - /** - * Returns if the current dataset is valid. - * - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return bool If the current dataset is valid - */ - public function valid() - { - return ( current( $this->data ) !== false ); - } - - /** - * Selects the very first dataset and returns it. - * This method is part of the Iterator interface to allow access to the - * datasets of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return ezcGraphDataSet The very first dataset. - */ - public function rewind() - { - return reset( $this->data ); - } - - /** - * Returns the number of datasets in the row. - * - * This method is part of the Countable interface to allow the usage of - * PHP's count() function to check how many datasets exist. - * - * @return int Number of datasets. - */ - public function count() - { - return count( $this->data ); - } -} -?> +chart = $chart; + } + + /** + * Adds a dataset to the charts data + * + * @param string $name Name of dataset + * @param ezcGraphDataSet $dataSet + * @param mixed $values Values to create dataset with + * @throws ezcGraphTooManyDataSetExceptions + * If too many datasets are created + * @return ezcGraphDataSet + */ + protected function addDataSet( $name, ezcGraphDataSet $dataSet ) + { + $this->data[$name] = $dataSet; + + $this->data[$name]->label = $name; + $this->data[$name]->palette = $this->chart->palette; + $this->data[$name]->displayType = $this->chart->getDefaultDisplayType(); + } + + /** + * Returns if the given offset exists. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key Identifier of dataset. + * @return bool True when the offset exists, otherwise false. + */ + public function offsetExists( $key ) + { + return isset( $this->data[$key] ); + } + + /** + * Returns the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key Identifier of dataset. + * @return ezcGraphDataSet + * + * @throws ezcGraphNoSuchDataSetException + * If no dataset with identifier exists + */ + public function offsetGet( $key ) + { + if ( !isset( $this->data[$key] ) ) + { + throw new ezcGraphNoSuchDataSetException( $key ); + } + + return $this->data[$key]; + } + + /** + * Set the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key + * @param ezcGraphDataSet $value + * @return void + * + * @throws ezcBaseValueException + * If supplied value is not an ezcGraphDataSet + */ + public function offsetSet( $key, $value ) + { + if ( !$value instanceof ezcGraphDataSet ) + { + throw new ezcBaseValueException( $key, $value, 'ezcGraphDataSet' ); + } + + return $this->addDataSet( $key, $value ); + } + + /** + * Unset the element with the given offset. + * + * This method is part of the ArrayAccess interface to allow access to the + * data of this object as if it was an array. + * + * @param string $key + * @return void + */ + public function offsetUnset( $key ) + { + if ( !isset( $this->data[$key] ) ) + { + throw new ezcGraphNoSuchDataSetException( $key ); + } + + unset( $this->data[$key] ); + } + + /** + * Returns the currently selected dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return ezcGraphDataSet The currently selected dataset. + */ + public function current() + { + return current( $this->data ); + } + + /** + * Returns the next dataset and selects it or false on the last dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return mixed ezcGraphDataSet if the next dataset exists, or false. + */ + public function next() + { + return next( $this->data ); + } + + /** + * Returns the key of the currently selected dataset. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return int The key of the currently selected dataset. + */ + public function key() + { + return key( $this->data ); + } + + /** + * Returns if the current dataset is valid. + * + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return bool If the current dataset is valid + */ + public function valid() + { + return ( current( $this->data ) !== false ); + } + + /** + * Selects the very first dataset and returns it. + * This method is part of the Iterator interface to allow access to the + * datasets of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return ezcGraphDataSet The very first dataset. + */ + public function rewind() + { + return reset( $this->data ); + } + + /** + * Returns the number of datasets in the row. + * + * This method is part of the Countable interface to allow the usage of + * PHP's count() function to check how many datasets exist. + * + * @return int Number of datasets. + */ + public function count() + { + return count( $this->data ); + } +} +?> diff --git a/library/ezc/Graph/src/data_container/single.php b/library/ezc/Graph/src/data_container/single.php index 6005df9ec1..d24b375ee1 100644 --- a/library/ezc/Graph/src/data_container/single.php +++ b/library/ezc/Graph/src/data_container/single.php @@ -1,51 +1,51 @@ -data ) >= 1 && - !isset( $this->data[$name] ) ) - { - throw new ezcGraphTooManyDataSetsExceptions( $name ); - } - else - { - parent::addDataSet( $name, $dataSet ); - - // Resette palette color counter - $this->chart->palette->resetColorCounter(); - - // Colorize each data element - foreach ( $this->data[$name] as $label => $value ) - { - $this->data[$name]->color[$label] = $this->chart->palette->dataSetColor; - } - } - } -} -?> +data ) >= 1 && + !isset( $this->data[$name] ) ) + { + throw new ezcGraphTooManyDataSetsExceptions( $name ); + } + else + { + parent::addDataSet( $name, $dataSet ); + + // Resette palette color counter + $this->chart->palette->resetColorCounter(); + + // Colorize each data element + foreach ( $this->data[$name] as $label => $value ) + { + $this->data[$name]->color[$label] = $this->chart->palette->dataSetColor; + } + } + } +} +?> diff --git a/library/ezc/Graph/src/datasets/array.php b/library/ezc/Graph/src/datasets/array.php index 3370d53fbc..56cc18643d 100644 --- a/library/ezc/Graph/src/datasets/array.php +++ b/library/ezc/Graph/src/datasets/array.php @@ -1,71 +1,71 @@ -createFromArray( $data ); - parent::__construct(); - } - - /** - * setData - * - * Can handle data provided through an array or iterator. - * - * @param array|Iterator $data - * @access public - * @return void - */ - protected function createFromArray( $data = array() ) - { - if ( !is_array( $data ) && - !( $data instanceof Traversable ) ) - { - throw new ezcGraphInvalidArrayDataSourceException( $data ); - } - - $this->data = array(); - foreach ( $data as $key => $value ) - { - $this->data[$key] = $value; - } - - if ( !count( $this->data ) ) - { - throw new ezcGraphInvalidDataException( 'Data sets should contain some values.' ); - } - } - - /** - * Returns the number of elements in this dataset - * - * @return int - */ - public function count() - { - return count( $this->data ); - } -} - -?> +createFromArray( $data ); + parent::__construct(); + } + + /** + * setData + * + * Can handle data provided through an array or iterator. + * + * @param array|Iterator $data + * @access public + * @return void + */ + protected function createFromArray( $data = array() ) + { + if ( !is_array( $data ) && + !( $data instanceof Traversable ) ) + { + throw new ezcGraphInvalidArrayDataSourceException( $data ); + } + + $this->data = array(); + foreach ( $data as $key => $value ) + { + $this->data[$key] = $value; + } + + if ( !count( $this->data ) ) + { + throw new ezcGraphInvalidDataException( 'Data sets should contain some values.' ); + } + } + + /** + * Returns the number of elements in this dataset + * + * @return int + */ + public function count() + { + return count( $this->data ); + } +} + +?> diff --git a/library/ezc/Graph/src/datasets/average.php b/library/ezc/Graph/src/datasets/average.php index a4e8fc53d6..89f7c671de 100644 --- a/library/ezc/Graph/src/datasets/average.php +++ b/library/ezc/Graph/src/datasets/average.php @@ -1,359 +1,359 @@ -mixed) - */ - protected $properties; - - /** - * Constructor - * - * @param ezcGraphDataSet $dataset Dataset to interpolate - * @param int $order Maximum order of interpolating polynom - * @return void - * @ignore - */ - public function __construct( ezcGraphDataSet $dataset, $order = 3 ) - { - parent::__construct(); - - $this->properties['resolution'] = 100; - $this->properties['polynomOrder'] = (int) $order; - - $this->source = $dataset; - } - - /** - * Options write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param mixed $propertyName Option name - * @param mixed $propertyValue Option value; - * @return mixed - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'polynomOrder': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 0' ); - } - - $this->properties['polynomOrder'] = (int) $propertyValue; - $this->polynom = false; - break; - case 'resolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); - } - - $this->properties['resolution'] = (int) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Property get access. - * Simply returns a given option. - * - * @param string $propertyName The name of the option to get. - * @return mixed The option value. - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - */ - public function __get( $propertyName ) - { - if ( array_key_exists( $propertyName, $this->properties ) ) - { - return $this->properties[$propertyName]; - } - return parent::__get( $propertyName ); - } - - /** - * Build the polynom based on the given points. - * - * @return void - */ - protected function buildPolynom() - { - $points = array(); - - foreach ( $this->source as $key => $value ) - { - if ( !is_numeric( $key ) ) - { - throw new ezcGraphDatasetAverageInvalidKeysException(); - } - - if ( ( $this->min === false ) || ( $this->min > $key ) ) - { - $this->min = (float) $key; - } - - if ( ( $this->max === false ) || ( $this->max < $key ) ) - { - $this->max = (float) $key; - } - - $points[] = new ezcGraphCoordinate( (float) $key, (float) $value ); - } - - // Build transposed and normal Matrix out of coordiantes - $a = new ezcGraphMatrix( count( $points ), $this->polynomOrder + 1 ); - $b = new ezcGraphMatrix( count( $points ), 1 ); - - for ( $i = 0; $i <= $this->properties['polynomOrder']; ++$i ) - { - foreach ( $points as $nr => $point ) - { - $a->set( $nr, $i, pow( $point->x, $i ) ); - $b->set( $nr, 0, $point->y ); - } - } - - $at = clone $a; - $at->transpose(); - - $left = $at->multiply( $a ); - $right = $at->multiply( $b ); - - $this->polynom = $left->solveNonlinearEquatation( $right ); - } - - /** - * Returns a polynom of the defined order witch matches the datapoints - * using the least squares algorithm. - * - * @return ezcGraphPolynom Polynom - */ - public function getPolynom() - { - if ( $this->polynom === false ) - { - $this->buildPolynom(); - } - - return $this->polynom; - } - - /** - * Get the x coordinate for the current position - * - * @param int $position Position - * @return float x coordinate - */ - protected function getKey() - { - $polynom = $this->getPolynom(); - return $this->min + - ( $this->max - $this->min ) / $this->resolution * $this->position; - } - - /** - * Returns true if the given datapoint exists - * Allows isset() using ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return bool Wether the key exists. - */ - public function offsetExists( $key ) - { - $polynom = $this->getPolynom(); - return ( ( $key >= $this->min ) && ( $key <= $this->max ) ); - } - - /** - * Returns the value for the given datapoint - * Get an datapoint value by ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return float The datapoint value. - */ - public function offsetGet( $key ) - { - $polynom = $this->getPolynom(); - return $polynom->evaluate( $key ); - } - - /** - * Throws a ezcBasePropertyPermissionException because single datapoints - * cannot be set in average datasets. - * - * @param string $key The kex of a datapoint to set. - * @param float $value The value for the datapoint. - * @throws ezcBasePropertyPermissionException - * Always, because access is readonly. - * @return void - */ - public function offsetSet( $key, $value ) - { - throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ ); - } - - /** - * Returns the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The currently selected datapoint. - */ - final public function current() - { - $polynom = $this->getPolynom(); - return $polynom->evaluate( $this->getKey() ); - } - - /** - * Returns the next datapoint and selects it or false on the last datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float datapoint if it exists, or false. - */ - final public function next() - { - if ( ++$this->position >= $this->resolution ) - { - return false; - } - else - { - return $this->current(); - } - } - - /** - * Returns the key of the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The key of the currently selected datapoint. - */ - final public function key() - { - return (string) $this->getKey(); - } - - /** - * Returns if the current datapoint is valid. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return bool If the current datapoint is valid - */ - final public function valid() - { - $polynom = $this->getPolynom(); - - if ( $this->min >= $this->max ) - { - return false; - } - - return ( ( $this->getKey() >= $this->min ) && ( $this->getKey() <= $this->max ) ); - } - - /** - * Selects the very first datapoint and returns it. - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float The very first datapoint. - */ - final public function rewind() - { - $this->position = 0; - } - - /** - * Returns the number of elements in this dataset - * - * @return int - */ - public function count() - { - return $this->resolution; - } -} -?> +mixed) + */ + protected $properties; + + /** + * Constructor + * + * @param ezcGraphDataSet $dataset Dataset to interpolate + * @param int $order Maximum order of interpolating polynom + * @return void + * @ignore + */ + public function __construct( ezcGraphDataSet $dataset, $order = 3 ) + { + parent::__construct(); + + $this->properties['resolution'] = 100; + $this->properties['polynomOrder'] = (int) $order; + + $this->source = $dataset; + } + + /** + * Options write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param mixed $propertyName Option name + * @param mixed $propertyValue Option value; + * @return mixed + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'polynomOrder': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 0' ); + } + + $this->properties['polynomOrder'] = (int) $propertyValue; + $this->polynom = false; + break; + case 'resolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); + } + + $this->properties['resolution'] = (int) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Property get access. + * Simply returns a given option. + * + * @param string $propertyName The name of the option to get. + * @return mixed The option value. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + */ + public function __get( $propertyName ) + { + if ( array_key_exists( $propertyName, $this->properties ) ) + { + return $this->properties[$propertyName]; + } + return parent::__get( $propertyName ); + } + + /** + * Build the polynom based on the given points. + * + * @return void + */ + protected function buildPolynom() + { + $points = array(); + + foreach ( $this->source as $key => $value ) + { + if ( !is_numeric( $key ) ) + { + throw new ezcGraphDatasetAverageInvalidKeysException(); + } + + if ( ( $this->min === false ) || ( $this->min > $key ) ) + { + $this->min = (float) $key; + } + + if ( ( $this->max === false ) || ( $this->max < $key ) ) + { + $this->max = (float) $key; + } + + $points[] = new ezcGraphCoordinate( (float) $key, (float) $value ); + } + + // Build transposed and normal Matrix out of coordiantes + $a = new ezcGraphMatrix( count( $points ), $this->polynomOrder + 1 ); + $b = new ezcGraphMatrix( count( $points ), 1 ); + + for ( $i = 0; $i <= $this->properties['polynomOrder']; ++$i ) + { + foreach ( $points as $nr => $point ) + { + $a->set( $nr, $i, pow( $point->x, $i ) ); + $b->set( $nr, 0, $point->y ); + } + } + + $at = clone $a; + $at->transpose(); + + $left = $at->multiply( $a ); + $right = $at->multiply( $b ); + + $this->polynom = $left->solveNonlinearEquatation( $right ); + } + + /** + * Returns a polynom of the defined order witch matches the datapoints + * using the least squares algorithm. + * + * @return ezcGraphPolynom Polynom + */ + public function getPolynom() + { + if ( $this->polynom === false ) + { + $this->buildPolynom(); + } + + return $this->polynom; + } + + /** + * Get the x coordinate for the current position + * + * @param int $position Position + * @return float x coordinate + */ + protected function getKey() + { + $polynom = $this->getPolynom(); + return $this->min + + ( $this->max - $this->min ) / $this->resolution * $this->position; + } + + /** + * Returns true if the given datapoint exists + * Allows isset() using ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return bool Wether the key exists. + */ + public function offsetExists( $key ) + { + $polynom = $this->getPolynom(); + return ( ( $key >= $this->min ) && ( $key <= $this->max ) ); + } + + /** + * Returns the value for the given datapoint + * Get an datapoint value by ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return float The datapoint value. + */ + public function offsetGet( $key ) + { + $polynom = $this->getPolynom(); + return $polynom->evaluate( $key ); + } + + /** + * Throws a ezcBasePropertyPermissionException because single datapoints + * cannot be set in average datasets. + * + * @param string $key The kex of a datapoint to set. + * @param float $value The value for the datapoint. + * @throws ezcBasePropertyPermissionException + * Always, because access is readonly. + * @return void + */ + public function offsetSet( $key, $value ) + { + throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ ); + } + + /** + * Returns the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The currently selected datapoint. + */ + final public function current() + { + $polynom = $this->getPolynom(); + return $polynom->evaluate( $this->getKey() ); + } + + /** + * Returns the next datapoint and selects it or false on the last datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float datapoint if it exists, or false. + */ + final public function next() + { + if ( ++$this->position >= $this->resolution ) + { + return false; + } + else + { + return $this->current(); + } + } + + /** + * Returns the key of the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The key of the currently selected datapoint. + */ + final public function key() + { + return (string) $this->getKey(); + } + + /** + * Returns if the current datapoint is valid. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return bool If the current datapoint is valid + */ + final public function valid() + { + $polynom = $this->getPolynom(); + + if ( $this->min >= $this->max ) + { + return false; + } + + return ( ( $this->getKey() >= $this->min ) && ( $this->getKey() <= $this->max ) ); + } + + /** + * Selects the very first datapoint and returns it. + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float The very first datapoint. + */ + final public function rewind() + { + $this->position = 0; + } + + /** + * Returns the number of elements in this dataset + * + * @return int + */ + public function count() + { + return $this->resolution; + } +} +?> diff --git a/library/ezc/Graph/src/datasets/base.php b/library/ezc/Graph/src/datasets/base.php index 8f625ed4aa..385f677e4a 100644 --- a/library/ezc/Graph/src/datasets/base.php +++ b/library/ezc/Graph/src/datasets/base.php @@ -1,302 +1,302 @@ -properties['label'] = new ezcGraphDataSetStringProperty( $this ); - $this->properties['color'] = new ezcGraphDataSetColorProperty( $this ); - $this->properties['symbol'] = new ezcGraphDataSetIntProperty( $this ); - $this->properties['lineThickness'] = new ezcGraphDataSetIntProperty( $this ); - $this->properties['highlight'] = new ezcGraphDataSetBooleanProperty( $this ); - $this->properties['highlightValue'] = new ezcGraphDataSetStringProperty( $this ); - $this->properties['displayType'] = new ezcGraphDataSetIntProperty( $this ); - $this->properties['url'] = new ezcGraphDataSetStringProperty( $this ); - - $this->properties['xAxis'] = new ezcGraphDataSetAxisProperty( $this ); - $this->properties['yAxis'] = new ezcGraphDataSetAxisProperty( $this ); - - $this->properties['highlight']->default = false; - } - - /** - * Options write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param mixed $propertyName Option name - * @param mixed $propertyValue Option value; - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'hilight': - $propertyName = 'highlight'; - case 'label': - case 'url': - case 'color': - case 'symbol': - case 'lineThickness': - case 'highlight': - case 'highlightValue': - case 'displayType': - case 'xAxis': - case 'yAxis': - $this->properties[$propertyName]->default = $propertyValue; - break; - - case 'palette': - $this->palette = $propertyValue; - $this->color->default = $this->palette->dataSetColor; - $this->symbol->default = $this->palette->dataSetSymbol; - break; - - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * Property get access. - * Simply returns a given option. - * - * @param string $propertyName The name of the option to get. - * @return mixed The option value. - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - */ - public function __get( $propertyName ) - { - if ( array_key_exists( $propertyName, $this->properties ) ) - { - return $this->properties[$propertyName]; - } - else - { - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - - /** - * Returns true if the given datapoint exists - * Allows isset() using ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return bool Wether the key exists. - */ - public function offsetExists( $key ) - { - return isset( $this->data[$key] ); - } - - /** - * Returns the value for the given datapoint - * Get an datapoint value by ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return float The datapoint value. - */ - public function offsetGet( $key ) - { - return $this->data[$key]; - } - - /** - * Sets the value for a datapoint. - * Sets an datapoint using ArrayAccess. - * - * @param string $key The kex of a datapoint to set. - * @param float $value The value for the datapoint. - * @return void - */ - public function offsetSet( $key, $value ) - { - $this->data[$key] = (float) $value; - } - - /** - * Unset an option. - * Unsets an option using ArrayAccess. - * - * @param string $key The options to unset. - * @return void - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @throws ezcBaseValueException - * If a the value for a property is out of range. - */ - public function offsetUnset( $key ) - { - unset( $this->data[$key] ); - } - - /** - * Returns the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The currently selected datapoint. - */ - public function current() - { - if ( !isset( $this->current ) ) - { - $this->keys = array_keys( $this->data ); - $this->current = 0; - } - - return $this->data[$this->keys[$this->current]]; - } - - /** - * Returns the next datapoint and selects it or false on the last datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float datapoint if it exists, or false. - */ - public function next() - { - if ( ++$this->current >= count( $this->keys ) ) - { - return false; - } - else - { - return $this->data[$this->keys[$this->current]]; - } - } - - /** - * Returns the key of the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The key of the currently selected datapoint. - */ - public function key() - { - return $this->keys[$this->current]; - } - - /** - * Returns if the current datapoint is valid. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return bool If the current datapoint is valid - */ - public function valid() - { - return isset( $this->keys[$this->current] ); - } - - /** - * Selects the very first datapoint and returns it. - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float The very first datapoint. - */ - public function rewind() - { - $this->keys = array_keys( $this->data ); - $this->current = 0; - } -} - -?> +properties['label'] = new ezcGraphDataSetStringProperty( $this ); + $this->properties['color'] = new ezcGraphDataSetColorProperty( $this ); + $this->properties['symbol'] = new ezcGraphDataSetIntProperty( $this ); + $this->properties['lineThickness'] = new ezcGraphDataSetIntProperty( $this ); + $this->properties['highlight'] = new ezcGraphDataSetBooleanProperty( $this ); + $this->properties['highlightValue'] = new ezcGraphDataSetStringProperty( $this ); + $this->properties['displayType'] = new ezcGraphDataSetIntProperty( $this ); + $this->properties['url'] = new ezcGraphDataSetStringProperty( $this ); + + $this->properties['xAxis'] = new ezcGraphDataSetAxisProperty( $this ); + $this->properties['yAxis'] = new ezcGraphDataSetAxisProperty( $this ); + + $this->properties['highlight']->default = false; + } + + /** + * Options write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param mixed $propertyName Option name + * @param mixed $propertyValue Option value; + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'hilight': + $propertyName = 'highlight'; + case 'label': + case 'url': + case 'color': + case 'symbol': + case 'lineThickness': + case 'highlight': + case 'highlightValue': + case 'displayType': + case 'xAxis': + case 'yAxis': + $this->properties[$propertyName]->default = $propertyValue; + break; + + case 'palette': + $this->palette = $propertyValue; + $this->color->default = $this->palette->dataSetColor; + $this->symbol->default = $this->palette->dataSetSymbol; + break; + + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * Property get access. + * Simply returns a given option. + * + * @param string $propertyName The name of the option to get. + * @return mixed The option value. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + */ + public function __get( $propertyName ) + { + if ( array_key_exists( $propertyName, $this->properties ) ) + { + return $this->properties[$propertyName]; + } + else + { + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + + /** + * Returns true if the given datapoint exists + * Allows isset() using ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return bool Wether the key exists. + */ + public function offsetExists( $key ) + { + return isset( $this->data[$key] ); + } + + /** + * Returns the value for the given datapoint + * Get an datapoint value by ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return float The datapoint value. + */ + public function offsetGet( $key ) + { + return $this->data[$key]; + } + + /** + * Sets the value for a datapoint. + * Sets an datapoint using ArrayAccess. + * + * @param string $key The kex of a datapoint to set. + * @param float $value The value for the datapoint. + * @return void + */ + public function offsetSet( $key, $value ) + { + $this->data[$key] = (float) $value; + } + + /** + * Unset an option. + * Unsets an option using ArrayAccess. + * + * @param string $key The options to unset. + * @return void + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @throws ezcBaseValueException + * If a the value for a property is out of range. + */ + public function offsetUnset( $key ) + { + unset( $this->data[$key] ); + } + + /** + * Returns the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The currently selected datapoint. + */ + public function current() + { + if ( !isset( $this->current ) ) + { + $this->keys = array_keys( $this->data ); + $this->current = 0; + } + + return $this->data[$this->keys[$this->current]]; + } + + /** + * Returns the next datapoint and selects it or false on the last datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float datapoint if it exists, or false. + */ + public function next() + { + if ( ++$this->current >= count( $this->keys ) ) + { + return false; + } + else + { + return $this->data[$this->keys[$this->current]]; + } + } + + /** + * Returns the key of the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The key of the currently selected datapoint. + */ + public function key() + { + return $this->keys[$this->current]; + } + + /** + * Returns if the current datapoint is valid. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return bool If the current datapoint is valid + */ + public function valid() + { + return isset( $this->keys[$this->current] ); + } + + /** + * Selects the very first datapoint and returns it. + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float The very first datapoint. + */ + public function rewind() + { + $this->keys = array_keys( $this->data ); + $this->current = 0; + } +} + +?> diff --git a/library/ezc/Graph/src/datasets/numeric.php b/library/ezc/Graph/src/datasets/numeric.php index 81d534c795..11590c363b 100644 --- a/library/ezc/Graph/src/datasets/numeric.php +++ b/library/ezc/Graph/src/datasets/numeric.php @@ -1,287 +1,287 @@ -mixed) - */ - protected $properties; - - /** - * Constructor - * - * @param float $start Start value for x axis values of function - * @param float $end End value for x axis values of function - * @param callback $callback Callback function - * @return void - * @ignore - */ - public function __construct( $start = null, $end = null, $callback = null ) - { - parent::__construct(); - - $this->properties['start'] = null; - $this->properties['end'] = null; - $this->properties['callback'] = null; - - if ( $start !== null ) - { - $this->start = $start; - } - - if ( $end !== null ) - { - $this->end = $end; - } - - if ( $callback !== null ) - { - $this->callback = $callback; - } - - $this->properties['resolution'] = 100; - } - - /** - * Options write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param mixed $propertyName Option name - * @param mixed $propertyValue Option value; - * @return mixed - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'resolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); - } - - $this->properties['resolution'] = (int) $propertyValue; - break; - case 'start': - case 'end': - if ( !is_numeric( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - case 'callback': - if ( !is_callable( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback' ); - } - - $this->properties[$propertyName] = $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Property get access. - * Simply returns a given option. - * - * @param string $propertyName The name of the option to get. - * @return mixed The option value. - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - */ - public function __get( $propertyName ) - { - if ( array_key_exists( $propertyName, $this->properties ) ) - { - return $this->properties[$propertyName]; - } - return parent::__get( $propertyName ); - } - - /** - * Get the x coordinate for the current position - * - * @param int $position Position - * @return float x coordinate - */ - protected function getKey() - { - return $this->start + - ( $this->end - $this->start ) / $this->resolution * $this->position; - } - - /** - * Returns true if the given datapoint exists - * Allows isset() using ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return bool Wether the key exists. - */ - public function offsetExists( $key ) - { - return ( ( $key >= $this->start ) && ( $key <= $this->end ) ); - } - - /** - * Returns the value for the given datapoint - * Get an datapoint value by ArrayAccess. - * - * @param string $key The key of the datapoint to get. - * @return float The datapoint value. - */ - public function offsetGet( $key ) - { - return call_user_func( $this->callback, $key ); - } - - /** - * Throws a ezcBasePropertyPermissionException because single datapoints - * cannot be set in average datasets. - * - * @param string $key The kex of a datapoint to set. - * @param float $value The value for the datapoint. - * @throws ezcBasePropertyPermissionException - * Always, because access is readonly. - * @return void - */ - public function offsetSet( $key, $value ) - { - throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ ); - } - - /** - * Returns the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The currently selected datapoint. - */ - final public function current() - { - return call_user_func( $this->callback, $this->getKey() ); - } - - /** - * Returns the next datapoint and selects it or false on the last datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float datapoint if it exists, or false. - */ - final public function next() - { - if ( $this->start === $this->end ) - { - throw new ezcGraphDatasetAverageInvalidKeysException(); - } - - if ( ++$this->position >= $this->resolution ) - { - return false; - } - else - { - return $this->current(); - } - } - - /** - * Returns the key of the currently selected datapoint. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return string The key of the currently selected datapoint. - */ - final public function key() - { - return (string) $this->getKey(); - } - - /** - * Returns if the current datapoint is valid. - * - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return bool If the current datapoint is valid - */ - final public function valid() - { - return ( ( $this->getKey() >= $this->start ) && ( $this->getKey() <= $this->end ) ); - } - - /** - * Selects the very first datapoint and returns it. - * This method is part of the Iterator interface to allow access to the - * datapoints of this row by iterating over it like an array (e.g. using - * foreach). - * - * @return float The very first datapoint. - */ - final public function rewind() - { - $this->position = 0; - } - - /** - * Returns the number of elements in this dataset - * - * @return int - */ - public function count() - { - return $this->resolution + 1; - } -} -?> +mixed) + */ + protected $properties; + + /** + * Constructor + * + * @param float $start Start value for x axis values of function + * @param float $end End value for x axis values of function + * @param callback $callback Callback function + * @return void + * @ignore + */ + public function __construct( $start = null, $end = null, $callback = null ) + { + parent::__construct(); + + $this->properties['start'] = null; + $this->properties['end'] = null; + $this->properties['callback'] = null; + + if ( $start !== null ) + { + $this->start = $start; + } + + if ( $end !== null ) + { + $this->end = $end; + } + + if ( $callback !== null ) + { + $this->callback = $callback; + } + + $this->properties['resolution'] = 100; + } + + /** + * Options write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param mixed $propertyName Option name + * @param mixed $propertyValue Option value; + * @return mixed + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'resolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' ); + } + + $this->properties['resolution'] = (int) $propertyValue; + break; + case 'start': + case 'end': + if ( !is_numeric( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + case 'callback': + if ( !is_callable( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback' ); + } + + $this->properties[$propertyName] = $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Property get access. + * Simply returns a given option. + * + * @param string $propertyName The name of the option to get. + * @return mixed The option value. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + */ + public function __get( $propertyName ) + { + if ( array_key_exists( $propertyName, $this->properties ) ) + { + return $this->properties[$propertyName]; + } + return parent::__get( $propertyName ); + } + + /** + * Get the x coordinate for the current position + * + * @param int $position Position + * @return float x coordinate + */ + protected function getKey() + { + return $this->start + + ( $this->end - $this->start ) / $this->resolution * $this->position; + } + + /** + * Returns true if the given datapoint exists + * Allows isset() using ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return bool Wether the key exists. + */ + public function offsetExists( $key ) + { + return ( ( $key >= $this->start ) && ( $key <= $this->end ) ); + } + + /** + * Returns the value for the given datapoint + * Get an datapoint value by ArrayAccess. + * + * @param string $key The key of the datapoint to get. + * @return float The datapoint value. + */ + public function offsetGet( $key ) + { + return call_user_func( $this->callback, $key ); + } + + /** + * Throws a ezcBasePropertyPermissionException because single datapoints + * cannot be set in average datasets. + * + * @param string $key The kex of a datapoint to set. + * @param float $value The value for the datapoint. + * @throws ezcBasePropertyPermissionException + * Always, because access is readonly. + * @return void + */ + public function offsetSet( $key, $value ) + { + throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ ); + } + + /** + * Returns the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The currently selected datapoint. + */ + final public function current() + { + return call_user_func( $this->callback, $this->getKey() ); + } + + /** + * Returns the next datapoint and selects it or false on the last datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float datapoint if it exists, or false. + */ + final public function next() + { + if ( $this->start === $this->end ) + { + throw new ezcGraphDatasetAverageInvalidKeysException(); + } + + if ( ++$this->position >= $this->resolution ) + { + return false; + } + else + { + return $this->current(); + } + } + + /** + * Returns the key of the currently selected datapoint. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return string The key of the currently selected datapoint. + */ + final public function key() + { + return (string) $this->getKey(); + } + + /** + * Returns if the current datapoint is valid. + * + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return bool If the current datapoint is valid + */ + final public function valid() + { + return ( ( $this->getKey() >= $this->start ) && ( $this->getKey() <= $this->end ) ); + } + + /** + * Selects the very first datapoint and returns it. + * This method is part of the Iterator interface to allow access to the + * datapoints of this row by iterating over it like an array (e.g. using + * foreach). + * + * @return float The very first datapoint. + */ + final public function rewind() + { + $this->position = 0; + } + + /** + * Returns the number of elements in this dataset + * + * @return int + */ + public function count() + { + return $this->resolution + 1; + } +} +?> diff --git a/library/ezc/Graph/src/datasets/property/axis.php b/library/ezc/Graph/src/datasets/property/axis.php index ed817c608f..bcb1c00e77 100644 --- a/library/ezc/Graph/src/datasets/property/axis.php +++ b/library/ezc/Graph/src/datasets/property/axis.php @@ -1,62 +1,62 @@ - + diff --git a/library/ezc/Graph/src/datasets/property/boolean.php b/library/ezc/Graph/src/datasets/property/boolean.php index 07d118a400..ab4b9e8ff5 100644 --- a/library/ezc/Graph/src/datasets/property/boolean.php +++ b/library/ezc/Graph/src/datasets/property/boolean.php @@ -1,37 +1,37 @@ - + diff --git a/library/ezc/Graph/src/datasets/property/color.php b/library/ezc/Graph/src/datasets/property/color.php index 532ef140ab..2bea337d3d 100644 --- a/library/ezc/Graph/src/datasets/property/color.php +++ b/library/ezc/Graph/src/datasets/property/color.php @@ -1,37 +1,37 @@ - + diff --git a/library/ezc/Graph/src/datasets/property/integer.php b/library/ezc/Graph/src/datasets/property/integer.php index 05ac14d115..3eda50fa9d 100644 --- a/library/ezc/Graph/src/datasets/property/integer.php +++ b/library/ezc/Graph/src/datasets/property/integer.php @@ -1,37 +1,37 @@ - + diff --git a/library/ezc/Graph/src/datasets/property/string.php b/library/ezc/Graph/src/datasets/property/string.php index 8acf50bf3c..69a0b13a4b 100644 --- a/library/ezc/Graph/src/datasets/property/string.php +++ b/library/ezc/Graph/src/datasets/property/string.php @@ -1,37 +1,37 @@ - + diff --git a/library/ezc/Graph/src/driver/cairo.php b/library/ezc/Graph/src/driver/cairo.php index 9beec26bc8..7a90dfc7ac 100644 --- a/library/ezc/Graph/src/driver/cairo.php +++ b/library/ezc/Graph/src/driver/cairo.php @@ -1,1010 +1,1010 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->background->color = '#FFFFFFFF'; - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->renderer = new ezcGraphRenderer3d(); - * $graph->renderer->options->pieChartShadowSize = 10; - * $graph->renderer->options->pieChartGleam = .5; - * $graph->renderer->options->dataBorder = false; - * $graph->renderer->options->pieChartHeight = 16; - * $graph->renderer->options->legendSymbolGleam = .5; - * - * // Use cairo driver - * $graph->driver = new ezcGraphCairoDriver(); - * - * $graph->render( 400, 200, 'tutorial_driver_cairo.png' ); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphCairoDriver extends ezcGraphDriver -{ - /** - * Surface for cairo - * - * @var resource - */ - protected $surface; - - /** - * Current cairo context. - * - * @var resource - */ - protected $context; - - /** - * List of strings to draw - * array ( array( - * 'text' => array( 'strings' ), - * 'options' => ezcGraphFontOptions, - * ) - * - * @var array - */ - protected $strings = array(); - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'cairo_wrapper' ); - $this->options = new ezcGraphCairoDriverOptions( $options ); - } - - /** - * Initilize cairo surface - * - * Initilize cairo surface from values provided in the options object, if - * is has not been already initlized. - * - * @return void - */ - protected function initiliazeSurface() - { - // Immediatly exit, if surface already exists - if ( $this->surface !== null ) - { - return; - } - - $this->surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, - $this->options->width, - $this->options->height - ); - - $this->context = cairo_create( $this->surface ); - cairo_set_line_width( $this->context, 1 ); - } - - /** - * Get SVG style definition - * - * Returns a string with SVG style definitions created from color, - * fillstatus and line thickness. - * - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @param float $thickness Line thickness. - * @return string Formatstring - */ - protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - switch ( true ) - { - case $color instanceof ezcGraphLinearGradient: - $pattern = cairo_pattern_create_linear( - $color->startPoint->x, $color->startPoint->y, - $color->endPoint->x, $color->endPoint->y - ); - - cairo_pattern_add_color_stop_rgba ( - $pattern, - 0, - $color->startColor->red / 255, - $color->startColor->green / 255, - $color->startColor->blue / 255, - 1 - $color->startColor->alpha / 255 - ); - - cairo_pattern_add_color_stop_rgba ( - $pattern, - 1, - $color->endColor->red / 255, - $color->endColor->green / 255, - $color->endColor->blue / 255, - 1 - $color->endColor->alpha / 255 - ); - - cairo_set_source( $this->context, $pattern ); - cairo_fill( $this->context ); - break; - - case $color instanceof ezcGraphRadialGradient: - $pattern = cairo_pattern_create_radial( - 0, 0, 0, - 0, 0, 1 - ); - - cairo_pattern_add_color_stop_rgba ( - $pattern, - 0, - $color->startColor->red / 255, - $color->startColor->green / 255, - $color->startColor->blue / 255, - 1 - $color->startColor->alpha / 255 - ); - - cairo_pattern_add_color_stop_rgba ( - $pattern, - 1, - $color->endColor->red / 255, - $color->endColor->green / 255, - $color->endColor->blue / 255, - 1 - $color->endColor->alpha / 255 - ); - - // Scale pattern, and move it to the correct position - $matrix = cairo_matrix_multiply( - $move = cairo_matrix_create_translate( -$color->center->x, -$color->center->y ), - $scale = cairo_matrix_create_scale( 1 / $color->width, 1 / $color->height ) - ); - cairo_pattern_set_matrix( $pattern, $matrix ); - - cairo_set_source( $this->context, $pattern ); - cairo_fill( $this->context ); - break; - default: - cairo_set_source_rgba( - $this->context, - $color->red / 255, - $color->green / 255, - $color->blue / 255, - 1 - $color->alpha / 255 - ); - break; - } - - // Set line width - cairo_set_line_width( $this->context, $thickness ); - - // Set requested fill state for context - if ( $filled ) - { - cairo_fill_preserve( $this->context ); - } - } - - /** - * Draws a single polygon. - * - * @param array $points Point array - * @param ezcGraphColor $color Polygon color - * @param mixed $filled Filled - * @param float $thickness Line thickness - * @return void - */ - public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - $this->initiliazeSurface(); - - $path = cairo_new_path( $this->context ); - - $lastPoint = end( $points ); - cairo_move_to( $this->context, $lastPoint->x, $lastPoint->y ); - - foreach ( $points as $point ) - { - cairo_line_to( $this->context, $point->x, $point->y ); - } - - cairo_close_path( $this->context ); - - $this->getStyle( $color, $filled, $thickness ); - cairo_stroke( $this->context ); - - return $points; - } - - /** - * Draws a line - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Line color - * @param float $thickness Line thickness - * @return void - */ - public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) - { - $this->initiliazeSurface(); - - $path = cairo_new_path( $this->context ); - - cairo_move_to( $this->context, $start->x, $start->y ); - cairo_line_to( $this->context, $end->x, $end->y ); - - $this->getStyle( $color, false, $thickness ); - cairo_stroke( $this->context ); - - return array( $start, $end ); - } - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) - { - cairo_select_font_face( $this->context, $font->name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL ); - cairo_set_font_size( $this->context, $size ); - $extents = cairo_text_extents( $this->context, $text ); - - return new ezcGraphBoundings( - 0, - 0, - $extents['width'], - $extents['height'] - ); - } - - /** - * Writes text in a box of desired size - * - * @param string $string Text - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of text box - * @param float $height Height of text box - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) - { - $this->initiliazeSurface(); - - $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); - - $width -= $padding * 2; - $height -= $padding * 2; - $textPosition = new ezcGraphCoordinate( - $position->x + $padding, - $position->y + $padding - ); - - // Try to get a font size for the text to fit into the box - $maxSize = min( $height, $this->options->font->maxFontSize ); - $result = false; - for ( $size = $maxSize; $size >= $this->options->font->minFontSize; ) - { - $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); - if ( is_array( $result ) ) - { - break; - } - $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) ); - } - - if ( !is_array( $result ) ) - { - if ( ( $height >= $this->options->font->minFontSize ) && - ( $this->options->autoShortenString ) ) - { - $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); - } - else - { - throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); - } - } - - $this->options->font->minimalUsedFont = $size; - $this->strings[] = array( - 'text' => $result, - 'position' => $textPosition, - 'width' => $width, - 'height' => $height, - 'align' => $align, - 'font' => $this->options->font, - 'rotation' => $rotation, - ); - - return array( - clone $position, - new ezcGraphCoordinate( $position->x + $width, $position->y ), - new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), - new ezcGraphCoordinate( $position->x, $position->y + $height ), - ); - } - - /** - * Render text depending of font type and available font extensions - * - * @param string $id - * @param string $text - * @param string $font - * @param ezcGraphColor $color - * @param ezcGraphCoordinate $position - * @param float $size - * @param float $rotation - * @return void - */ - protected function renderText( $text, $font, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null ) - { - cairo_select_font_face( $this->context, $font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL ); - cairo_set_font_size( $this->context, $size ); - - // Store current state of context - cairo_save( $this->context ); - cairo_move_to( $this->context, 0, 0 ); - - if ( $rotation !== null ) - { - // Move to the center - cairo_translate( $this->context, - $rotation->getCenter()->x, - $rotation->getCenter()->y - ); - // Rotate around text center - cairo_rotate( $this->context, - deg2rad( $rotation->getRotation() ) - ); - // Center the text - cairo_translate( $this->context, - $position->x - $rotation->getCenter()->x, - $position->y - $rotation->getCenter()->y - $size * .15 - ); - } else { - cairo_translate( $this->context, - $position->x, - $position->y - $size * .15 - ); - } - - cairo_new_path( $this->context ); - $this->getStyle( $color, true ); - cairo_show_text( $this->context, $text ); - cairo_stroke( $this->context ); - - // Restore state of context - cairo_restore( $this->context ); - } - - /** - * Draw all collected texts - * - * The texts are collected and their maximum possible font size is - * calculated. This function finally draws the texts on the image, this - * delayed drawing has two reasons: - * - * 1) This way the text strings are always on top of the image, what - * results in better readable texts - * 2) The maximum possible font size can be calculated for a set of texts - * with the same font configuration. Strings belonging to one chart - * element normally have the same font configuration, so that all texts - * belonging to one element will have the same font size. - * - * @access protected - * @return void - */ - protected function drawAllTexts() - { - $this->initiliazeSurface(); - - foreach ( $this->strings as $text ) - { - $size = $text['font']->minimalUsedFont; - - $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; - - // Calculate y offset for vertical alignement - switch ( true ) - { - case ( $text['align'] & ezcGraph::BOTTOM ): - $yOffset = $text['height'] - $completeHeight; - break; - case ( $text['align'] & ezcGraph::MIDDLE ): - $yOffset = ( $text['height'] - $completeHeight ) / 2; - break; - case ( $text['align'] & ezcGraph::TOP ): - default: - $yOffset = 0; - break; - } - - $padding = $text['font']->padding + $text['font']->borderWidth / 2; - if ( $this->options->font->minimizeBorder === true ) - { - // Calculate maximum width of text rows - $width = false; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - if ( ( $width === false) || ( $boundings->width > $width ) ) - { - $width = $boundings->width; - } - } - - switch ( true ) - { - case ( $text['align'] & ezcGraph::CENTER ): - $xOffset = ( $text['width'] - $width ) / 2; - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $xOffset = $text['width'] - $width; - break; - case ( $text['align'] & ezcGraph::LEFT ): - default: - $xOffset = 0; - break; - } - - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - ); - } - else - { - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y + $padding * 2 + $text['height'] - ), - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y + $padding * 2 + $text['height'] - ), - ); - } - - if ( $text['rotation'] !== null ) - { - foreach ( $borderPolygonArray as $nr => $point ) - { - $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); - } - } - - if ( $text['font']->background !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->background, - true - ); - } - - if ( $text['font']->border !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->border, - false, - $text['font']->borderWidth - ); - } - - // Render text with evaluated font size - $completeString = ''; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $completeString .= $string; - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - $text['position']->y += $size; - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $position = new ezcGraphCoordinate( - $text['position']->x, - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( $text['width'] - $boundings->width ), - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::CENTER ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), - $text['position']->y + $yOffset - ); - break; - } - - // Optionally draw text shadow - if ( $text['font']->textShadow === true ) - { - $this->renderText( - $string, - $text['font']->name, - $text['font']->textShadowColor, - new ezcGraphCoordinate( - $position->x + $text['font']->textShadowOffset, - $position->y + $text['font']->textShadowOffset - ), - $size, - $text['rotation'] - ); - } - - // Finally draw text - $this->renderText( - $string, - $text['font']->name, - $text['font']->color, - $position, - $size, - $text['rotation'] - ); - - $text['position']->y += $size * $this->options->lineSpacing; - } - } - } - - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center Center of circle - * @param mixed $width Width - * @param mixed $height Height - * @param mixed $startAngle Start angle of circle sector - * @param mixed $endAngle End angle of circle sector - * @param ezcGraphColor $color Color - * @param mixed $filled Filled; - * @return void - */ - public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $this->initiliazeSurface(); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - cairo_save( $this->context ); - - // Draw circular arc path - $path = cairo_new_path( $this->context ); - cairo_translate( $this->context, - $center->x, - $center->y - ); - cairo_scale( $this->context, - 1, $height / $width - ); - - cairo_move_to( $this->context, 0, 0 ); - cairo_arc( $this->context, - 0., 0., - $width / 2, - deg2rad( $startAngle ), - deg2rad( $endAngle ) - ); - cairo_line_to( $this->context, 0, 0 ); - - cairo_restore( $this->context ); - $this->getStyle( $color, $filled ); - cairo_stroke( $this->context ); - - // Create polygon array to return - $polygonArray = array( $center ); - for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ); - - return $polygonArray; - } - - /** - * Draws a circular arc consisting of several minor steps on the bounding - * lines. - * - * @param ezcGraphCoordinate $center - * @param mixed $width - * @param mixed $height - * @param mixed $size - * @param mixed $startAngle - * @param mixed $endAngle - * @param ezcGraphColor $color - * @param bool $filled - * @return string Element id - */ - protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled ) - { - for ( - $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle ); - $tmpAngle <= $endAngle; - $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) ) - { - $path = cairo_new_path( $this->context ); - cairo_move_to( $this->context, - $center->x + cos( deg2rad( $startAngle ) ) * $width / 2, - $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 - ); - - // @TODO: Use cairo_curve_to() - for( - $angle = $startAngle; - $angle <= $tmpAngle; - $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) ) - { - cairo_line_to( $this->context, - $center->x + cos( deg2rad( $angle ) ) * $width / 2, - $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size - ); - - if ( $angle === $tmpAngle ) - { - break; - } - } - - for( - $angle = $tmpAngle; - $angle >= $startAngle; - $angle = max( $angle - $this->options->circleResolution, $startAngle ) ) - { - cairo_line_to( $this->context, - $center->x + cos( deg2rad( $angle ) ) * $width / 2, - $center->y + sin( deg2rad( $angle ) ) * $height / 2 - ); - - if ( $angle === $startAngle ) - { - break; - } - } - - cairo_close_path( $this->context ); - $this->getStyle( $color, $filled ); - cairo_stroke( $this->context ); - - $startAngle = $tmpAngle; - if ( $tmpAngle === $endAngle ) - { - break; - } - } - } - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled - * @return void - */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $this->initiliazeSurface(); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled ); - - if ( ( $this->options->shadeCircularArc !== false ) && - $filled ) - { - $gradient = new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $center->x - $width, - $center->y - ), - new ezcGraphCoordinate( - $center->x + $width, - $center->y - ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), - ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 ) - ); - - $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled ); - } - - // Create polygon array to return - $polygonArray = array(); - for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ); - - for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size, - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size, - $center->y + - ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) - ); - - return $polygonArray; - } - - /** - * Draw circle - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param mixed $width Width of ellipse - * @param mixed $height height of ellipse - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) - { - $this->initiliazeSurface(); - - cairo_save( $this->context ); - - // Draw circular arc path - $path = cairo_new_path( $this->context ); - cairo_translate( $this->context, - $center->x, - $center->y - ); - cairo_scale( $this->context, - 1, $height / $width - ); - - cairo_arc( $this->context, - 0., 0., - $width / 2, - 0, 2 * M_PI - ); - - cairo_restore( $this->context ); - $this->getStyle( $color, $filled ); - cairo_stroke( $this->context ); - - // Create polygon array to return - $polygonArray = array(); - for ( $angle = 0; $angle < ( 2 * M_PI ); $angle += deg2rad( $this->options->imageMapResolution ) ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( $angle ) * $width ) / 2 ), - $center->y + - ( ( sin( $angle ) * $height ) / 2 ) - ); - } - - return $polygonArray; - } - - /** - * Draw an image - * - * The image will be inlined in the SVG document using data URL scheme. For - * this the mime type and base64 encoded file content will be merged to - * URL. - * - * @param mixed $file Image file - * @param ezcGraphCoordinate $position Top left position - * @param mixed $width Width of image in destination image - * @param mixed $height Height of image in destination image - * @return void - */ - public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) - { - $this->initiliazeSurface(); - - // Ensure given bitmap is a PNG image - $data = getimagesize( $file ); - if ( $data[2] !== IMAGETYPE_PNG ) - { - throw new Exception( 'Cairo only has support for PNGs.' ); - } - - // Create new surface from given bitmap - $imageSurface = cairo_image_surface_create_from_png( $file ); - - // Create pattern from source image to be able to transform it - $pattern = cairo_pattern_create_for_surface( $imageSurface ); - - // Scale pattern to defined dimensions and move it to its destination position - $matrix = cairo_matrix_multiply( - $move = cairo_matrix_create_translate( -$position->x, -$position->y ), - $scale = cairo_matrix_create_scale( $data[0] / $width, $data[1] / $height ) - ); - cairo_pattern_set_matrix( $pattern, $matrix ); - - // Merge surfaces - cairo_set_source( $this->context, $pattern ); - cairo_rectangle( $this->context, $position->x, $position->y, $width, $height ); - cairo_fill( $this->context ); - } - - /** - * Return mime type for current image format - * - * @return string - */ - public function getMimeType() - { - return 'image/png'; - } - - /** - * Render image directly to output - * - * The method renders the image directly to the standard output. You - * normally do not want to use this function, because it makes it harder - * to proper cache the generated graphs. - * - * @return void - */ - public function renderToOutput() - { - $this->drawAllTexts(); - - header( 'Content-Type: ' . $this->getMimeType() ); - - // Write to tmp file, echo and remove tmp file again. - $fileName = tempnam( '/tmp', 'ezc' ); - - // cairo_surface_write_to_png( $this->surface, $file ); - cairo_surface_write_to_png( $this->surface, $fileName ); - $contents = file_get_contents( $fileName ); - unlink( $fileName ); - - // Directly echo contents - echo $contents; - } - - /** - * Finally save image - * - * @param string $file Destination filename - * @return void - */ - public function render( $file ) - { - $this->drawAllTexts(); - cairo_surface_write_to_png( $this->surface, $file ); - } - - /** - * Get resource of rendered result - * - * Return the resource of the rendered result. You should not use this - * method before you called either renderToOutput() or render(), as the - * image may not be completely rendered until then. - * - * This method returns an array, containing the surface and the context in - * a structure like: - * - * array( - * 'surface' => resource, - * 'context' => resource, - * ) - * - * - * @return array - */ - public function getResource() - { - return array( - 'surface' => $this->surface, - 'context' => $this->context, - ); - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->background->color = '#FFFFFFFF'; + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->renderer = new ezcGraphRenderer3d(); + * $graph->renderer->options->pieChartShadowSize = 10; + * $graph->renderer->options->pieChartGleam = .5; + * $graph->renderer->options->dataBorder = false; + * $graph->renderer->options->pieChartHeight = 16; + * $graph->renderer->options->legendSymbolGleam = .5; + * + * // Use cairo driver + * $graph->driver = new ezcGraphCairoDriver(); + * + * $graph->render( 400, 200, 'tutorial_driver_cairo.png' ); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphCairoDriver extends ezcGraphDriver +{ + /** + * Surface for cairo + * + * @var resource + */ + protected $surface; + + /** + * Current cairo context. + * + * @var resource + */ + protected $context; + + /** + * List of strings to draw + * array ( array( + * 'text' => array( 'strings' ), + * 'options' => ezcGraphFontOptions, + * ) + * + * @var array + */ + protected $strings = array(); + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'cairo_wrapper' ); + $this->options = new ezcGraphCairoDriverOptions( $options ); + } + + /** + * Initilize cairo surface + * + * Initilize cairo surface from values provided in the options object, if + * is has not been already initlized. + * + * @return void + */ + protected function initiliazeSurface() + { + // Immediatly exit, if surface already exists + if ( $this->surface !== null ) + { + return; + } + + $this->surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + $this->options->width, + $this->options->height + ); + + $this->context = cairo_create( $this->surface ); + cairo_set_line_width( $this->context, 1 ); + } + + /** + * Get SVG style definition + * + * Returns a string with SVG style definitions created from color, + * fillstatus and line thickness. + * + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @param float $thickness Line thickness. + * @return string Formatstring + */ + protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + switch ( true ) + { + case $color instanceof ezcGraphLinearGradient: + $pattern = cairo_pattern_create_linear( + $color->startPoint->x, $color->startPoint->y, + $color->endPoint->x, $color->endPoint->y + ); + + cairo_pattern_add_color_stop_rgba ( + $pattern, + 0, + $color->startColor->red / 255, + $color->startColor->green / 255, + $color->startColor->blue / 255, + 1 - $color->startColor->alpha / 255 + ); + + cairo_pattern_add_color_stop_rgba ( + $pattern, + 1, + $color->endColor->red / 255, + $color->endColor->green / 255, + $color->endColor->blue / 255, + 1 - $color->endColor->alpha / 255 + ); + + cairo_set_source( $this->context, $pattern ); + cairo_fill( $this->context ); + break; + + case $color instanceof ezcGraphRadialGradient: + $pattern = cairo_pattern_create_radial( + 0, 0, 0, + 0, 0, 1 + ); + + cairo_pattern_add_color_stop_rgba ( + $pattern, + 0, + $color->startColor->red / 255, + $color->startColor->green / 255, + $color->startColor->blue / 255, + 1 - $color->startColor->alpha / 255 + ); + + cairo_pattern_add_color_stop_rgba ( + $pattern, + 1, + $color->endColor->red / 255, + $color->endColor->green / 255, + $color->endColor->blue / 255, + 1 - $color->endColor->alpha / 255 + ); + + // Scale pattern, and move it to the correct position + $matrix = cairo_matrix_multiply( + $move = cairo_matrix_create_translate( -$color->center->x, -$color->center->y ), + $scale = cairo_matrix_create_scale( 1 / $color->width, 1 / $color->height ) + ); + cairo_pattern_set_matrix( $pattern, $matrix ); + + cairo_set_source( $this->context, $pattern ); + cairo_fill( $this->context ); + break; + default: + cairo_set_source_rgba( + $this->context, + $color->red / 255, + $color->green / 255, + $color->blue / 255, + 1 - $color->alpha / 255 + ); + break; + } + + // Set line width + cairo_set_line_width( $this->context, $thickness ); + + // Set requested fill state for context + if ( $filled ) + { + cairo_fill_preserve( $this->context ); + } + } + + /** + * Draws a single polygon. + * + * @param array $points Point array + * @param ezcGraphColor $color Polygon color + * @param mixed $filled Filled + * @param float $thickness Line thickness + * @return void + */ + public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + $this->initiliazeSurface(); + + $path = cairo_new_path( $this->context ); + + $lastPoint = end( $points ); + cairo_move_to( $this->context, $lastPoint->x, $lastPoint->y ); + + foreach ( $points as $point ) + { + cairo_line_to( $this->context, $point->x, $point->y ); + } + + cairo_close_path( $this->context ); + + $this->getStyle( $color, $filled, $thickness ); + cairo_stroke( $this->context ); + + return $points; + } + + /** + * Draws a line + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Line color + * @param float $thickness Line thickness + * @return void + */ + public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) + { + $this->initiliazeSurface(); + + $path = cairo_new_path( $this->context ); + + cairo_move_to( $this->context, $start->x, $start->y ); + cairo_line_to( $this->context, $end->x, $end->y ); + + $this->getStyle( $color, false, $thickness ); + cairo_stroke( $this->context ); + + return array( $start, $end ); + } + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) + { + cairo_select_font_face( $this->context, $font->name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL ); + cairo_set_font_size( $this->context, $size ); + $extents = cairo_text_extents( $this->context, $text ); + + return new ezcGraphBoundings( + 0, + 0, + $extents['width'], + $extents['height'] + ); + } + + /** + * Writes text in a box of desired size + * + * @param string $string Text + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of text box + * @param float $height Height of text box + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) + { + $this->initiliazeSurface(); + + $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); + + $width -= $padding * 2; + $height -= $padding * 2; + $textPosition = new ezcGraphCoordinate( + $position->x + $padding, + $position->y + $padding + ); + + // Try to get a font size for the text to fit into the box + $maxSize = min( $height, $this->options->font->maxFontSize ); + $result = false; + for ( $size = $maxSize; $size >= $this->options->font->minFontSize; ) + { + $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); + if ( is_array( $result ) ) + { + break; + } + $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) ); + } + + if ( !is_array( $result ) ) + { + if ( ( $height >= $this->options->font->minFontSize ) && + ( $this->options->autoShortenString ) ) + { + $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); + } + else + { + throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); + } + } + + $this->options->font->minimalUsedFont = $size; + $this->strings[] = array( + 'text' => $result, + 'position' => $textPosition, + 'width' => $width, + 'height' => $height, + 'align' => $align, + 'font' => $this->options->font, + 'rotation' => $rotation, + ); + + return array( + clone $position, + new ezcGraphCoordinate( $position->x + $width, $position->y ), + new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), + new ezcGraphCoordinate( $position->x, $position->y + $height ), + ); + } + + /** + * Render text depending of font type and available font extensions + * + * @param string $id + * @param string $text + * @param string $font + * @param ezcGraphColor $color + * @param ezcGraphCoordinate $position + * @param float $size + * @param float $rotation + * @return void + */ + protected function renderText( $text, $font, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null ) + { + cairo_select_font_face( $this->context, $font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL ); + cairo_set_font_size( $this->context, $size ); + + // Store current state of context + cairo_save( $this->context ); + cairo_move_to( $this->context, 0, 0 ); + + if ( $rotation !== null ) + { + // Move to the center + cairo_translate( $this->context, + $rotation->getCenter()->x, + $rotation->getCenter()->y + ); + // Rotate around text center + cairo_rotate( $this->context, + deg2rad( $rotation->getRotation() ) + ); + // Center the text + cairo_translate( $this->context, + $position->x - $rotation->getCenter()->x, + $position->y - $rotation->getCenter()->y - $size * .15 + ); + } else { + cairo_translate( $this->context, + $position->x, + $position->y - $size * .15 + ); + } + + cairo_new_path( $this->context ); + $this->getStyle( $color, true ); + cairo_show_text( $this->context, $text ); + cairo_stroke( $this->context ); + + // Restore state of context + cairo_restore( $this->context ); + } + + /** + * Draw all collected texts + * + * The texts are collected and their maximum possible font size is + * calculated. This function finally draws the texts on the image, this + * delayed drawing has two reasons: + * + * 1) This way the text strings are always on top of the image, what + * results in better readable texts + * 2) The maximum possible font size can be calculated for a set of texts + * with the same font configuration. Strings belonging to one chart + * element normally have the same font configuration, so that all texts + * belonging to one element will have the same font size. + * + * @access protected + * @return void + */ + protected function drawAllTexts() + { + $this->initiliazeSurface(); + + foreach ( $this->strings as $text ) + { + $size = $text['font']->minimalUsedFont; + + $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; + + // Calculate y offset for vertical alignement + switch ( true ) + { + case ( $text['align'] & ezcGraph::BOTTOM ): + $yOffset = $text['height'] - $completeHeight; + break; + case ( $text['align'] & ezcGraph::MIDDLE ): + $yOffset = ( $text['height'] - $completeHeight ) / 2; + break; + case ( $text['align'] & ezcGraph::TOP ): + default: + $yOffset = 0; + break; + } + + $padding = $text['font']->padding + $text['font']->borderWidth / 2; + if ( $this->options->font->minimizeBorder === true ) + { + // Calculate maximum width of text rows + $width = false; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + if ( ( $width === false) || ( $boundings->width > $width ) ) + { + $width = $boundings->width; + } + } + + switch ( true ) + { + case ( $text['align'] & ezcGraph::CENTER ): + $xOffset = ( $text['width'] - $width ) / 2; + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $xOffset = $text['width'] - $width; + break; + case ( $text['align'] & ezcGraph::LEFT ): + default: + $xOffset = 0; + break; + } + + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + ); + } + else + { + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y + $padding * 2 + $text['height'] + ), + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y + $padding * 2 + $text['height'] + ), + ); + } + + if ( $text['rotation'] !== null ) + { + foreach ( $borderPolygonArray as $nr => $point ) + { + $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); + } + } + + if ( $text['font']->background !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->background, + true + ); + } + + if ( $text['font']->border !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->border, + false, + $text['font']->borderWidth + ); + } + + // Render text with evaluated font size + $completeString = ''; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $completeString .= $string; + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + $text['position']->y += $size; + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $position = new ezcGraphCoordinate( + $text['position']->x, + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( $text['width'] - $boundings->width ), + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::CENTER ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), + $text['position']->y + $yOffset + ); + break; + } + + // Optionally draw text shadow + if ( $text['font']->textShadow === true ) + { + $this->renderText( + $string, + $text['font']->name, + $text['font']->textShadowColor, + new ezcGraphCoordinate( + $position->x + $text['font']->textShadowOffset, + $position->y + $text['font']->textShadowOffset + ), + $size, + $text['rotation'] + ); + } + + // Finally draw text + $this->renderText( + $string, + $text['font']->name, + $text['font']->color, + $position, + $size, + $text['rotation'] + ); + + $text['position']->y += $size * $this->options->lineSpacing; + } + } + } + + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center Center of circle + * @param mixed $width Width + * @param mixed $height Height + * @param mixed $startAngle Start angle of circle sector + * @param mixed $endAngle End angle of circle sector + * @param ezcGraphColor $color Color + * @param mixed $filled Filled; + * @return void + */ + public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $this->initiliazeSurface(); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + cairo_save( $this->context ); + + // Draw circular arc path + $path = cairo_new_path( $this->context ); + cairo_translate( $this->context, + $center->x, + $center->y + ); + cairo_scale( $this->context, + 1, $height / $width + ); + + cairo_move_to( $this->context, 0, 0 ); + cairo_arc( $this->context, + 0., 0., + $width / 2, + deg2rad( $startAngle ), + deg2rad( $endAngle ) + ); + cairo_line_to( $this->context, 0, 0 ); + + cairo_restore( $this->context ); + $this->getStyle( $color, $filled ); + cairo_stroke( $this->context ); + + // Create polygon array to return + $polygonArray = array( $center ); + for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ); + + return $polygonArray; + } + + /** + * Draws a circular arc consisting of several minor steps on the bounding + * lines. + * + * @param ezcGraphCoordinate $center + * @param mixed $width + * @param mixed $height + * @param mixed $size + * @param mixed $startAngle + * @param mixed $endAngle + * @param ezcGraphColor $color + * @param bool $filled + * @return string Element id + */ + protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled ) + { + for ( + $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle ); + $tmpAngle <= $endAngle; + $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) ) + { + $path = cairo_new_path( $this->context ); + cairo_move_to( $this->context, + $center->x + cos( deg2rad( $startAngle ) ) * $width / 2, + $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 + ); + + // @TODO: Use cairo_curve_to() + for( + $angle = $startAngle; + $angle <= $tmpAngle; + $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) ) + { + cairo_line_to( $this->context, + $center->x + cos( deg2rad( $angle ) ) * $width / 2, + $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size + ); + + if ( $angle === $tmpAngle ) + { + break; + } + } + + for( + $angle = $tmpAngle; + $angle >= $startAngle; + $angle = max( $angle - $this->options->circleResolution, $startAngle ) ) + { + cairo_line_to( $this->context, + $center->x + cos( deg2rad( $angle ) ) * $width / 2, + $center->y + sin( deg2rad( $angle ) ) * $height / 2 + ); + + if ( $angle === $startAngle ) + { + break; + } + } + + cairo_close_path( $this->context ); + $this->getStyle( $color, $filled ); + cairo_stroke( $this->context ); + + $startAngle = $tmpAngle; + if ( $tmpAngle === $endAngle ) + { + break; + } + } + } + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled + * @return void + */ + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $this->initiliazeSurface(); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled ); + + if ( ( $this->options->shadeCircularArc !== false ) && + $filled ) + { + $gradient = new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $center->x - $width, + $center->y + ), + new ezcGraphCoordinate( + $center->x + $width, + $center->y + ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), + ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 ) + ); + + $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled ); + } + + // Create polygon array to return + $polygonArray = array(); + for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ); + + for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size, + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size, + $center->y + + ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + ); + + return $polygonArray; + } + + /** + * Draw circle + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param mixed $width Width of ellipse + * @param mixed $height height of ellipse + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) + { + $this->initiliazeSurface(); + + cairo_save( $this->context ); + + // Draw circular arc path + $path = cairo_new_path( $this->context ); + cairo_translate( $this->context, + $center->x, + $center->y + ); + cairo_scale( $this->context, + 1, $height / $width + ); + + cairo_arc( $this->context, + 0., 0., + $width / 2, + 0, 2 * M_PI + ); + + cairo_restore( $this->context ); + $this->getStyle( $color, $filled ); + cairo_stroke( $this->context ); + + // Create polygon array to return + $polygonArray = array(); + for ( $angle = 0; $angle < ( 2 * M_PI ); $angle += deg2rad( $this->options->imageMapResolution ) ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( $angle ) * $width ) / 2 ), + $center->y + + ( ( sin( $angle ) * $height ) / 2 ) + ); + } + + return $polygonArray; + } + + /** + * Draw an image + * + * The image will be inlined in the SVG document using data URL scheme. For + * this the mime type and base64 encoded file content will be merged to + * URL. + * + * @param mixed $file Image file + * @param ezcGraphCoordinate $position Top left position + * @param mixed $width Width of image in destination image + * @param mixed $height Height of image in destination image + * @return void + */ + public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) + { + $this->initiliazeSurface(); + + // Ensure given bitmap is a PNG image + $data = getimagesize( $file ); + if ( $data[2] !== IMAGETYPE_PNG ) + { + throw new Exception( 'Cairo only has support for PNGs.' ); + } + + // Create new surface from given bitmap + $imageSurface = cairo_image_surface_create_from_png( $file ); + + // Create pattern from source image to be able to transform it + $pattern = cairo_pattern_create_for_surface( $imageSurface ); + + // Scale pattern to defined dimensions and move it to its destination position + $matrix = cairo_matrix_multiply( + $move = cairo_matrix_create_translate( -$position->x, -$position->y ), + $scale = cairo_matrix_create_scale( $data[0] / $width, $data[1] / $height ) + ); + cairo_pattern_set_matrix( $pattern, $matrix ); + + // Merge surfaces + cairo_set_source( $this->context, $pattern ); + cairo_rectangle( $this->context, $position->x, $position->y, $width, $height ); + cairo_fill( $this->context ); + } + + /** + * Return mime type for current image format + * + * @return string + */ + public function getMimeType() + { + return 'image/png'; + } + + /** + * Render image directly to output + * + * The method renders the image directly to the standard output. You + * normally do not want to use this function, because it makes it harder + * to proper cache the generated graphs. + * + * @return void + */ + public function renderToOutput() + { + $this->drawAllTexts(); + + header( 'Content-Type: ' . $this->getMimeType() ); + + // Write to tmp file, echo and remove tmp file again. + $fileName = tempnam( '/tmp', 'ezc' ); + + // cairo_surface_write_to_png( $this->surface, $file ); + cairo_surface_write_to_png( $this->surface, $fileName ); + $contents = file_get_contents( $fileName ); + unlink( $fileName ); + + // Directly echo contents + echo $contents; + } + + /** + * Finally save image + * + * @param string $file Destination filename + * @return void + */ + public function render( $file ) + { + $this->drawAllTexts(); + cairo_surface_write_to_png( $this->surface, $file ); + } + + /** + * Get resource of rendered result + * + * Return the resource of the rendered result. You should not use this + * method before you called either renderToOutput() or render(), as the + * image may not be completely rendered until then. + * + * This method returns an array, containing the surface and the context in + * a structure like: + * + * array( + * 'surface' => resource, + * 'context' => resource, + * ) + * + * + * @return array + */ + public function getResource() + { + return array( + 'surface' => $this->surface, + 'context' => $this->context, + ); + } +} + +?> diff --git a/library/ezc/Graph/src/driver/flash.php b/library/ezc/Graph/src/driver/flash.php index ea07b86a5e..9adde87b97 100644 --- a/library/ezc/Graph/src/driver/flash.php +++ b/library/ezc/Graph/src/driver/flash.php @@ -1,972 +1,972 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->driver = new ezcGraphFlashDriver(); - * $graph->options->font = 'tutorial_font.fdb'; - * - * $graph->driver->options->compression = 7; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->render( 400, 200, 'tutorial_driver_flash.swf' ); - * - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphFlashDriver extends ezcGraphDriver -{ - /** - * Flash movie - * - * @var SWFMovie - */ - protected $movie; - - /** - * Unique element id - * - * @var int - */ - protected $id = 1; - - /** - * Array with strings to draw later - * - * @var array - */ - protected $strings = array(); - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'ming' ); - $this->options = new ezcGraphFlashDriverOptions( $options ); - } - - /** - * Returns unique movie object as a parent canvas for all swf objects. - * - * @return SWFMovie - */ - public function getDocument() - { - if ( $this->movie === null ) - { - ming_setscale( 1.0 ); - $this->movie = new SWFMovie(); - $this->movie->setDimension( $this->modifyCoordinate( $this->options->width ), $this->modifyCoordinate( $this->options->height ) ); - $this->movie->setRate( 1 ); - $this->movie->setBackground( 255, 255, 255 ); - } - - return $this->movie; - } - - /** - * Set the fill and line properties for a SWWFShape according to the - * given parameters. - * - * @param SWFShape $shape - * @param ezcGraphColor $color - * @param mixed $thickness - * @param mixed $filled - * @return void - */ - protected function setShapeColor( SWFShape $shape, ezcGraphColor $color, $thickness, $filled ) - { - if ( $filled ) - { - switch ( true ) - { - case ( $color instanceof ezcGraphLinearGradient ): - $gradient = new SWFGradient(); - $gradient->addEntry( - 0, - $color->startColor->red, - $color->startColor->green, - $color->startColor->blue, - 255 - $color->startColor->alpha - ); - $gradient->addEntry( - 1, - $color->endColor->red, - $color->endColor->green, - $color->endColor->blue, - 255 - $color->endColor->alpha - ); - - $fill = $shape->addFill( $gradient, SWFFILL_LINEAR_GRADIENT ); - - // Calculate desired length of gradient - $length = sqrt( - pow( $color->endPoint->x - $color->startPoint->x, 2 ) + - pow( $color->endPoint->y - $color->startPoint->y, 2 ) - ); - - $fill->scaleTo( $this->modifyCoordinate( $length ) / 32768 , $this->modifyCoordinate( $length ) / 32768 ); - $fill->rotateTo( - rad2deg( asin( - ( $color->endPoint->x - $color->startPoint->x ) / $length - ) + 180 ) - ); - $fill->moveTo( - $this->modifyCoordinate( - ( $color->startPoint->x + $color->endPoint->x ) / 2 - ), - $this->modifyCoordinate( - ( $color->startPoint->y + $color->endPoint->y ) / 2 - ) - ); - - $shape->setLeftFill( $fill ); - break; - case ( $color instanceof ezcGraphRadialGradient ): - $gradient = new SWFGradient(); - $gradient->addEntry( - 0, - $color->startColor->red, - $color->startColor->green, - $color->startColor->blue, - 255 - $color->startColor->alpha - ); - $gradient->addEntry( - 1, - $color->endColor->red, - $color->endColor->green, - $color->endColor->blue, - 255 - $color->endColor->alpha - ); - - $fill = $shape->addFill( $gradient, SWFFILL_RADIAL_GRADIENT ); - - $fill->scaleTo( $this->modifyCoordinate( $color->width ) / 32768, $this->modifyCoordinate( $color->height ) / 32768 ); - $fill->moveTo( $this->modifyCoordinate( $color->center->x ), $this->modifyCoordinate( $color->center->y ) ); - - $shape->setLeftFill( $fill ); - break; - default: - $fill = $shape->addFill( $color->red, $color->green, $color->blue, 255 - $color->alpha ); - $shape->setLeftFill( $fill ); - break; - } - } - else - { - $shape->setLine( $this->modifyCoordinate( $thickness ), $color->red, $color->green, $color->blue, 255 - $color->alpha ); - } - } - - /** - * Modifies a coordinate value, as flash usally uses twips instead of - * pixels for a higher solution, as it only accepts integer values. - * - * @param float $pointValue - * @return float - */ - protected function modifyCoordinate( $pointValue ) - { - return $pointValue * 10; - } - - /** - * Demodifies a coordinate value, as flash usally uses twips instead of - * pixels for a higher solution, as it only accepts integer values. - * - * @param float $pointValue - * @return float - */ - protected function deModifyCoordinate( $pointValue ) - { - return $pointValue / 10; - } - - /** - * Draws a single polygon. - * - * @param array $points Point array - * @param ezcGraphColor $color Polygon color - * @param mixed $filled Filled - * @param float $thickness Line thickness - * @return void - */ - public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - $movie = $this->getDocument(); - - if ( !$filled ) - { - // The middle of the border is on the outline of a polygon in ming, - // fix that: - try - { - $points = $this->reducePolygonSize( $points, $thickness / 2 ); - } - catch ( ezcGraphReducementFailedException $e ) - { - return false; - } - } - - $shape = new SWFShape(); - - $this->setShapeColor( $shape, $color, $thickness, $filled ); - - $lastPoint = end( $points ); - $shape->movePenTo( $this->modifyCoordinate( $lastPoint->x ), $this->modifyCoordinate( $lastPoint->y ) ); - - foreach ( $points as $point ) - { - $shape->drawLineTo( $this->modifyCoordinate( $point->x ), $this->modifyCoordinate( $point->y ) ); - } - - $object = $movie->add( $shape ); - $object->setName( $id = 'ezcGraphPolygon_' . $this->id++ ); - - return $id; - } - - /** - * Draws a line - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Line color - * @param float $thickness Line thickness - * @return void - */ - public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) - { - $movie = $this->getDocument(); - - $shape = new SWFShape(); - - $this->setShapeColor( $shape, $color, $thickness, false ); - - $shape->movePenTo( $this->modifyCoordinate( $start->x ), $this->modifyCoordinate( $start->y ) ); - $shape->drawLineTo( $this->modifyCoordinate( $end->x ), $this->modifyCoordinate( $end->y ) ); - - $object = $movie->add( $shape ); - $object->setName( $id = 'ezcGraphLine_' . $this->id++ ); - - return $id; - } - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) - { - $t = new SWFText(); - $t->setFont( new SWFFont( $font->path ) ); - $t->setHeight( $size ); - - $boundings = new ezcGraphBoundings( 0, 0, $t->getWidth( $text ), $size ); - - return $boundings; - } - - /** - * Writes text in a box of desired size - * - * @param string $string Text - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of text box - * @param float $height Height of text box - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) - { - $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); - - $width = $this->modifyCoordinate( $width - $padding * 2 ); - $height = $this->modifyCoordinate( $height - $padding * 2 ); - $position = new ezcGraphCoordinate( - $this->modifyCoordinate( $position->x + $padding ), - $this->modifyCoordinate( $position->y + $padding ) - ); - - // Try to get a font size for the text to fit into the box - $maxSize = $this->modifyCoordinate( min( $height, $this->options->font->maxFontSize ) ); - $minSize = $this->modifyCoordinate( $this->options->font->minFontSize ); - $result = false; - for ( $size = $maxSize; $size >= $minSize; ) - { - $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); - if ( is_array( $result ) ) - { - break; - } - $size = $this->deModifyCoordinate( $size ); - $size = $this->modifyCoordinate( floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ) ); - } - - if ( !is_array( $result ) ) - { - if ( ( $height >= $this->options->font->minFontSize ) && - ( $this->options->autoShortenString ) ) - { - $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->modifyCoordinate( $this->options->font->minFontSize ) ); - } - else - { - throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); - } - } - - - $this->options->font->minimalUsedFont = $this->deModifyCoordinate( $size ); - - $this->strings[] = array( - 'text' => $result, - 'id' => $id = 'ezcGraphTextBox_' . $this->id++, - 'position' => $position, - 'width' => $width, - 'height' => $height, - 'align' => $align, - 'font' => $this->options->font, - 'rotation' => $rotation, - ); - - return $id; - } - - /** - * Render text depending of font type and available font extensions - * - * @param string $id - * @param string $text - * @param string $chars - * @param int $type - * @param string $path - * @param ezcGraphColor $color - * @param ezcGraphCoordinate $position - * @param float $size - * @param float $rotation - * @return void - */ - protected function renderText( $id, $text, $chars, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null ) - { - $movie = $this->getDocument(); - - $tb = new SWFTextField( SWFTEXTFIELD_NOEDIT ); - $tb->setFont( new SWFFont( $path ) ); - $tb->setHeight( $size ); - $tb->setColor( $color->red, $color->green, $color->blue, 255 - $color->alpha ); - $tb->addString( $text ); - $tb->addChars( $chars ); - - $object = $movie->add( $tb ); - $object->rotate( - ( $rotation !== null ? -$rotation->getRotation() : 0 ) - ); - $object->moveTo( - $position->x + - ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 0, 2 ) ) ), - $position->y - - $size * ( 1 + $this->options->lineSpacing ) + - ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 1, 2 ) ) ) - ); - $object->setName( $id ); - } - - /** - * Draw all collected texts - * - * The texts are collected and their maximum possible font size is - * calculated. This function finally draws the texts on the image, this - * delayed drawing has two reasons: - * - * 1) This way the text strings are always on top of the image, what - * results in better readable texts - * 2) The maximum possible font size can be calculated for a set of texts - * with the same font configuration. Strings belonging to one chart - * element normally have the same font configuration, so that all texts - * belonging to one element will have the same font size. - * - * @access protected - * @return void - */ - protected function drawAllTexts() - { - // Iterate over all strings to collect used chars per font - $chars = array(); - foreach ( $this->strings as $text ) - { - $completeString = ''; - foreach ( $text['text'] as $line ) - { - $completeString .= implode( ' ', $line ); - } - - // Collect chars for each font - if ( !isset( $chars[$text['font']->path] ) ) - { - $chars[$text['font']->path] = $completeString; - } - else - { - $chars[$text['font']->path] .= $completeString; - } - } - - foreach ( $this->strings as $text ) - { - $size = $this->modifyCoordinate( $text['font']->minimalUsedFont ); - - $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; - - // Calculate y offset for vertical alignement - switch ( true ) - { - case ( $text['align'] & ezcGraph::BOTTOM ): - $yOffset = $text['height'] - $completeHeight; - break; - case ( $text['align'] & ezcGraph::MIDDLE ): - $yOffset = ( $text['height'] - $completeHeight ) / 2; - break; - case ( $text['align'] & ezcGraph::TOP ): - default: - $yOffset = 0; - break; - } - - $padding = $text['font']->padding + $text['font']->borderWidth / 2; - if ( $this->options->font->minimizeBorder === true ) - { - // Calculate maximum width of text rows - $width = false; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - if ( ( $width === false) || ( $boundings->width > $width ) ) - { - $width = $boundings->width; - } - } - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $xOffset = 0; - break; - case ( $text['align'] & ezcGraph::CENTER ): - $xOffset = ( $text['width'] - $width ) / 2; - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $xOffset = $text['width'] - $width; - break; - } - - $borderPolygonArray = array( - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ), - $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ), - $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ), - $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ), - $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight ) - ), - ); - } - else - { - $borderPolygonArray = array( - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x - $padding ), - $this->deModifyCoordinate( $text['position']->y - $padding ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ), - $this->deModifyCoordinate( $text['position']->y - $padding ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ), - $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] ) - ), - new ezcGraphCoordinate( - $this->deModifyCoordinate( $text['position']->x - $padding ), - $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] ) - ), - ); - } - - if ( $text['rotation'] !== null ) - { - foreach ( $borderPolygonArray as $nr => $point ) - { - $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); - } - } - - if ( $text['font']->background !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->background, - true - ); - } - - if ( $text['font']->border !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->border, - false, - $text['font']->borderWidth - ); - } - - // Render text with evaluated font size - $completeString = ''; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $completeString .= $string; - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - $text['position']->y += $size; - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $position = new ezcGraphCoordinate( - $text['position']->x, - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( $text['width'] - $boundings->width ), - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::CENTER ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), - $text['position']->y + $yOffset - ); - break; - } - - // Optionally draw text shadow - if ( $text['font']->textShadow === true ) - { - $this->renderText( - $text['id'], - $string, - $chars[$text['font']->path], - $text['font']->type, - $text['font']->path, - $text['font']->textShadowColor, - new ezcGraphCoordinate( - $position->x + $this->modifyCoordinate( $text['font']->textShadowOffset ), - $position->y + $this->modifyCoordinate( $text['font']->textShadowOffset ) - ), - $size, - $text['rotation'] - ); - } - - // Finally draw text - $this->renderText( - $text['id'], - $string, - $chars[$text['font']->path], - $text['font']->type, - $text['font']->path, - $text['font']->color, - $position, - $size, - $text['rotation'] - ); - - $text['position']->y += $size * $this->options->lineSpacing; - } - } - } - - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center Center of circle - * @param mixed $width Width - * @param mixed $height Height - * @param mixed $startAngle Start angle of circle sector - * @param mixed $endAngle End angle of circle sector - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - $movie = $this->getDocument(); - - $shape = new SWFShape(); - $this->setShapeColor( $shape, $color, 1, $filled ); - - if ( !$filled ) - { - try - { - $reduced = $this->reduceEllipseSize( $center, $width, $height, $startAngle, $endAngle, .5 ); - } - catch ( ezcGraphReducementFailedException $e ) - { - return false; - } - - $startAngle = $reduced['startAngle']; - $endAngle = $reduced['endAngle']; - - $width -= 1; - $height -= 1; - } - - $shape->movePenTo( $this->modifyCoordinate( $center->x ), $this->modifyCoordinate( $center->y ) ); - - // @TODO: User SWFShape::curveTo - for( - $angle = $startAngle; - $angle <= $endAngle; - $angle = min( $angle + $this->options->circleResolution, $endAngle ) ) - { - $shape->drawLineTo( - $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), - $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) - ); - - if ( $angle === $endAngle ) - { - break; - } - } - - $shape->drawLineTo( - $this->modifyCoordinate( $center->x ), - $this->modifyCoordinate( $center->y ) - ); - - $object = $movie->add( $shape ); - $object->setName( $id = 'ezcGraphCircleSector_' . $this->id++ ); - - return $id; - } - - /** - * Draws a circular arc consisting of several minor steps on the bounding - * lines. - * - * @param ezcGraphCoordinate $center - * @param mixed $width - * @param mixed $height - * @param mixed $size - * @param mixed $startAngle - * @param mixed $endAngle - * @param ezcGraphColor $color - * @param bool $filled - * @return string Element id - */ - protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled ) - { - $movie = $this->getDocument(); - $id = 'ezcGraphCircularArc_' . $this->id++; - - for ( - $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle ); - $tmpAngle <= $endAngle; - $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) ) - { - $shape = new SWFShape(); - $this->setShapeColor( $shape, $color, 1, $filled ); - - $shape->movePenTo( - $this->modifyCoordinate( $center->x + cos( deg2rad( $startAngle ) ) * $width / 2 ), - $this->modifyCoordinate( $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 ) - ); - - // @TODO: Use SWFShape::curveTo - for( - $angle = $startAngle; - $angle <= $tmpAngle; - $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) ) - { - $shape->drawLineTo( - $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), - $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size ) - ); - - if ( $angle === $tmpAngle ) - { - break; - } - } - - for( - $angle = $tmpAngle; - $angle >= $startAngle; - $angle = max( $angle - $this->options->circleResolution, $startAngle ) ) - { - $shape->drawLineTo( - $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), - $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) - ); - - if ( $angle === $startAngle ) - { - break; - } - } - - $object = $movie->add( $shape ); - $object->setName( $id ); - - $startAngle = $tmpAngle; - if ( $tmpAngle === $endAngle ) - { - break; - } - } - - return $id; - } - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled - * @return void - */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - $id = $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled ); - - if ( ( $this->options->shadeCircularArc !== false ) && - $filled ) - { - $gradient = new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $center->x - $width, - $center->y - ), - new ezcGraphCoordinate( - $center->x + $width, - $center->y - ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), - ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 ) - ); - - $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled ); - } - - return $id; - } - - /** - * Draw circle - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param mixed $width Width of ellipse - * @param mixed $height height of ellipse - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) - { - $movie = $this->getDocument(); - - $shape = new SWFShape(); - $this->setShapeColor( $shape, $color, 1, $filled ); - - // Reduce size - if ( !$filled ) - { - $width -= 1; - $height -= 1; - } - - $shape->movePenTo( - $this->modifyCoordinate( $center->x + $width / 2 ), - $this->modifyCoordinate( $center->y ) - ); - - // @TODO: User SWFShape::curveTo - for ( $angle = $this->options->circleResolution; $angle < 360; $angle += $this->options->circleResolution ) - { - $shape->drawLineTo( - $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), - $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) - ); - } - - $shape->drawLineTo( - $this->modifyCoordinate( $center->x + $width / 2 ), - $this->modifyCoordinate( $center->y ) - ); - - $object = $movie->add( $shape ); - $object->setName( $id = 'ezcGraphCircle_' . $this->id++ ); - - return $id; - } - - /** - * Draw an image - * - * The image will be inlined in the SVG document using data URL scheme. For - * this the mime type and base64 encoded file content will be merged to - * URL. - * - * @param mixed $file Image file - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of image in destination image - * @param float $height Height of image in destination image - * @return void - */ - public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) - { - $movie = $this->getDocument(); - - $imageData = getimagesize( $file ); - if ( ( $imageData[2] !== IMAGETYPE_JPEG ) && ( $imageData[2] !== IMAGETYPE_PNG ) ) - { - throw new ezcGraphFlashBitmapTypeException( $imageData[2] ); - } - - // Try to create a new SWFBitmap object from provided file - $bitmap = new SWFBitmap( fopen( $file, 'rb' ) ); - - // Add the image to the movie - $object = $this->movie->add( $bitmap ); - - // Image size is calculated on the base of a tick size of 20, so - // that we need to transform this, to our tick size. - $factor = $this->modifyCoordinate( 1 ) / 20; - $object->scale( $factor, $factor ); - - // Scale by ratio of requested and original image size - $object->scale( - $width / $imageData[0], - $height / $imageData[1] - ); - - // Move object to the right position - $object->moveTo( - $this->modifyCoordinate( $position->x ), - $this->modifyCoordinate( $position->y ) - ); - - // Create, set and return unique ID - $object->setName( $id = 'ezcGraphImage_'. $this->id++ ); - return $id; - } - - /** - * Return mime type for current image format - * - * @return string - */ - public function getMimeType() - { - return 'application/x-shockwave-flash'; - } - - /** - * Finally save image - * - * @param string $file Destination filename - * @return void - */ - public function render( $file ) - { - $this->drawAllTexts(); - $movie = $this->getDocument(); - $movie->save( $file, $this->options->compression ); - } - - /** - * Get resource of rendered result - * - * Return the resource of the rendered result. You should not use this - * method before you called either renderToOutput() or render(), as the - * image may not be completely rendered until then. - * - * @return SWFMovie - */ - public function getResource() - { - return $this->movie; - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->driver = new ezcGraphFlashDriver(); + * $graph->options->font = 'tutorial_font.fdb'; + * + * $graph->driver->options->compression = 7; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->render( 400, 200, 'tutorial_driver_flash.swf' ); + * + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphFlashDriver extends ezcGraphDriver +{ + /** + * Flash movie + * + * @var SWFMovie + */ + protected $movie; + + /** + * Unique element id + * + * @var int + */ + protected $id = 1; + + /** + * Array with strings to draw later + * + * @var array + */ + protected $strings = array(); + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'ming' ); + $this->options = new ezcGraphFlashDriverOptions( $options ); + } + + /** + * Returns unique movie object as a parent canvas for all swf objects. + * + * @return SWFMovie + */ + public function getDocument() + { + if ( $this->movie === null ) + { + ming_setscale( 1.0 ); + $this->movie = new SWFMovie(); + $this->movie->setDimension( $this->modifyCoordinate( $this->options->width ), $this->modifyCoordinate( $this->options->height ) ); + $this->movie->setRate( 1 ); + $this->movie->setBackground( 255, 255, 255 ); + } + + return $this->movie; + } + + /** + * Set the fill and line properties for a SWWFShape according to the + * given parameters. + * + * @param SWFShape $shape + * @param ezcGraphColor $color + * @param mixed $thickness + * @param mixed $filled + * @return void + */ + protected function setShapeColor( SWFShape $shape, ezcGraphColor $color, $thickness, $filled ) + { + if ( $filled ) + { + switch ( true ) + { + case ( $color instanceof ezcGraphLinearGradient ): + $gradient = new SWFGradient(); + $gradient->addEntry( + 0, + $color->startColor->red, + $color->startColor->green, + $color->startColor->blue, + 255 - $color->startColor->alpha + ); + $gradient->addEntry( + 1, + $color->endColor->red, + $color->endColor->green, + $color->endColor->blue, + 255 - $color->endColor->alpha + ); + + $fill = $shape->addFill( $gradient, SWFFILL_LINEAR_GRADIENT ); + + // Calculate desired length of gradient + $length = sqrt( + pow( $color->endPoint->x - $color->startPoint->x, 2 ) + + pow( $color->endPoint->y - $color->startPoint->y, 2 ) + ); + + $fill->scaleTo( $this->modifyCoordinate( $length ) / 32768 , $this->modifyCoordinate( $length ) / 32768 ); + $fill->rotateTo( + rad2deg( asin( + ( $color->endPoint->x - $color->startPoint->x ) / $length + ) + 180 ) + ); + $fill->moveTo( + $this->modifyCoordinate( + ( $color->startPoint->x + $color->endPoint->x ) / 2 + ), + $this->modifyCoordinate( + ( $color->startPoint->y + $color->endPoint->y ) / 2 + ) + ); + + $shape->setLeftFill( $fill ); + break; + case ( $color instanceof ezcGraphRadialGradient ): + $gradient = new SWFGradient(); + $gradient->addEntry( + 0, + $color->startColor->red, + $color->startColor->green, + $color->startColor->blue, + 255 - $color->startColor->alpha + ); + $gradient->addEntry( + 1, + $color->endColor->red, + $color->endColor->green, + $color->endColor->blue, + 255 - $color->endColor->alpha + ); + + $fill = $shape->addFill( $gradient, SWFFILL_RADIAL_GRADIENT ); + + $fill->scaleTo( $this->modifyCoordinate( $color->width ) / 32768, $this->modifyCoordinate( $color->height ) / 32768 ); + $fill->moveTo( $this->modifyCoordinate( $color->center->x ), $this->modifyCoordinate( $color->center->y ) ); + + $shape->setLeftFill( $fill ); + break; + default: + $fill = $shape->addFill( $color->red, $color->green, $color->blue, 255 - $color->alpha ); + $shape->setLeftFill( $fill ); + break; + } + } + else + { + $shape->setLine( $this->modifyCoordinate( $thickness ), $color->red, $color->green, $color->blue, 255 - $color->alpha ); + } + } + + /** + * Modifies a coordinate value, as flash usally uses twips instead of + * pixels for a higher solution, as it only accepts integer values. + * + * @param float $pointValue + * @return float + */ + protected function modifyCoordinate( $pointValue ) + { + return $pointValue * 10; + } + + /** + * Demodifies a coordinate value, as flash usally uses twips instead of + * pixels for a higher solution, as it only accepts integer values. + * + * @param float $pointValue + * @return float + */ + protected function deModifyCoordinate( $pointValue ) + { + return $pointValue / 10; + } + + /** + * Draws a single polygon. + * + * @param array $points Point array + * @param ezcGraphColor $color Polygon color + * @param mixed $filled Filled + * @param float $thickness Line thickness + * @return void + */ + public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + $movie = $this->getDocument(); + + if ( !$filled ) + { + // The middle of the border is on the outline of a polygon in ming, + // fix that: + try + { + $points = $this->reducePolygonSize( $points, $thickness / 2 ); + } + catch ( ezcGraphReducementFailedException $e ) + { + return false; + } + } + + $shape = new SWFShape(); + + $this->setShapeColor( $shape, $color, $thickness, $filled ); + + $lastPoint = end( $points ); + $shape->movePenTo( $this->modifyCoordinate( $lastPoint->x ), $this->modifyCoordinate( $lastPoint->y ) ); + + foreach ( $points as $point ) + { + $shape->drawLineTo( $this->modifyCoordinate( $point->x ), $this->modifyCoordinate( $point->y ) ); + } + + $object = $movie->add( $shape ); + $object->setName( $id = 'ezcGraphPolygon_' . $this->id++ ); + + return $id; + } + + /** + * Draws a line + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Line color + * @param float $thickness Line thickness + * @return void + */ + public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) + { + $movie = $this->getDocument(); + + $shape = new SWFShape(); + + $this->setShapeColor( $shape, $color, $thickness, false ); + + $shape->movePenTo( $this->modifyCoordinate( $start->x ), $this->modifyCoordinate( $start->y ) ); + $shape->drawLineTo( $this->modifyCoordinate( $end->x ), $this->modifyCoordinate( $end->y ) ); + + $object = $movie->add( $shape ); + $object->setName( $id = 'ezcGraphLine_' . $this->id++ ); + + return $id; + } + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) + { + $t = new SWFText(); + $t->setFont( new SWFFont( $font->path ) ); + $t->setHeight( $size ); + + $boundings = new ezcGraphBoundings( 0, 0, $t->getWidth( $text ), $size ); + + return $boundings; + } + + /** + * Writes text in a box of desired size + * + * @param string $string Text + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of text box + * @param float $height Height of text box + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) + { + $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); + + $width = $this->modifyCoordinate( $width - $padding * 2 ); + $height = $this->modifyCoordinate( $height - $padding * 2 ); + $position = new ezcGraphCoordinate( + $this->modifyCoordinate( $position->x + $padding ), + $this->modifyCoordinate( $position->y + $padding ) + ); + + // Try to get a font size for the text to fit into the box + $maxSize = $this->modifyCoordinate( min( $height, $this->options->font->maxFontSize ) ); + $minSize = $this->modifyCoordinate( $this->options->font->minFontSize ); + $result = false; + for ( $size = $maxSize; $size >= $minSize; ) + { + $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); + if ( is_array( $result ) ) + { + break; + } + $size = $this->deModifyCoordinate( $size ); + $size = $this->modifyCoordinate( floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ) ); + } + + if ( !is_array( $result ) ) + { + if ( ( $height >= $this->options->font->minFontSize ) && + ( $this->options->autoShortenString ) ) + { + $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->modifyCoordinate( $this->options->font->minFontSize ) ); + } + else + { + throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); + } + } + + + $this->options->font->minimalUsedFont = $this->deModifyCoordinate( $size ); + + $this->strings[] = array( + 'text' => $result, + 'id' => $id = 'ezcGraphTextBox_' . $this->id++, + 'position' => $position, + 'width' => $width, + 'height' => $height, + 'align' => $align, + 'font' => $this->options->font, + 'rotation' => $rotation, + ); + + return $id; + } + + /** + * Render text depending of font type and available font extensions + * + * @param string $id + * @param string $text + * @param string $chars + * @param int $type + * @param string $path + * @param ezcGraphColor $color + * @param ezcGraphCoordinate $position + * @param float $size + * @param float $rotation + * @return void + */ + protected function renderText( $id, $text, $chars, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null ) + { + $movie = $this->getDocument(); + + $tb = new SWFTextField( SWFTEXTFIELD_NOEDIT ); + $tb->setFont( new SWFFont( $path ) ); + $tb->setHeight( $size ); + $tb->setColor( $color->red, $color->green, $color->blue, 255 - $color->alpha ); + $tb->addString( $text ); + $tb->addChars( $chars ); + + $object = $movie->add( $tb ); + $object->rotate( + ( $rotation !== null ? -$rotation->getRotation() : 0 ) + ); + $object->moveTo( + $position->x + + ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 0, 2 ) ) ), + $position->y - + $size * ( 1 + $this->options->lineSpacing ) + + ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 1, 2 ) ) ) + ); + $object->setName( $id ); + } + + /** + * Draw all collected texts + * + * The texts are collected and their maximum possible font size is + * calculated. This function finally draws the texts on the image, this + * delayed drawing has two reasons: + * + * 1) This way the text strings are always on top of the image, what + * results in better readable texts + * 2) The maximum possible font size can be calculated for a set of texts + * with the same font configuration. Strings belonging to one chart + * element normally have the same font configuration, so that all texts + * belonging to one element will have the same font size. + * + * @access protected + * @return void + */ + protected function drawAllTexts() + { + // Iterate over all strings to collect used chars per font + $chars = array(); + foreach ( $this->strings as $text ) + { + $completeString = ''; + foreach ( $text['text'] as $line ) + { + $completeString .= implode( ' ', $line ); + } + + // Collect chars for each font + if ( !isset( $chars[$text['font']->path] ) ) + { + $chars[$text['font']->path] = $completeString; + } + else + { + $chars[$text['font']->path] .= $completeString; + } + } + + foreach ( $this->strings as $text ) + { + $size = $this->modifyCoordinate( $text['font']->minimalUsedFont ); + + $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; + + // Calculate y offset for vertical alignement + switch ( true ) + { + case ( $text['align'] & ezcGraph::BOTTOM ): + $yOffset = $text['height'] - $completeHeight; + break; + case ( $text['align'] & ezcGraph::MIDDLE ): + $yOffset = ( $text['height'] - $completeHeight ) / 2; + break; + case ( $text['align'] & ezcGraph::TOP ): + default: + $yOffset = 0; + break; + } + + $padding = $text['font']->padding + $text['font']->borderWidth / 2; + if ( $this->options->font->minimizeBorder === true ) + { + // Calculate maximum width of text rows + $width = false; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + if ( ( $width === false) || ( $boundings->width > $width ) ) + { + $width = $boundings->width; + } + } + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $xOffset = 0; + break; + case ( $text['align'] & ezcGraph::CENTER ): + $xOffset = ( $text['width'] - $width ) / 2; + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $xOffset = $text['width'] - $width; + break; + } + + $borderPolygonArray = array( + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ), + $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ), + $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ), + $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ), + $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight ) + ), + ); + } + else + { + $borderPolygonArray = array( + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x - $padding ), + $this->deModifyCoordinate( $text['position']->y - $padding ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ), + $this->deModifyCoordinate( $text['position']->y - $padding ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ), + $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] ) + ), + new ezcGraphCoordinate( + $this->deModifyCoordinate( $text['position']->x - $padding ), + $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] ) + ), + ); + } + + if ( $text['rotation'] !== null ) + { + foreach ( $borderPolygonArray as $nr => $point ) + { + $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); + } + } + + if ( $text['font']->background !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->background, + true + ); + } + + if ( $text['font']->border !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->border, + false, + $text['font']->borderWidth + ); + } + + // Render text with evaluated font size + $completeString = ''; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $completeString .= $string; + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + $text['position']->y += $size; + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $position = new ezcGraphCoordinate( + $text['position']->x, + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( $text['width'] - $boundings->width ), + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::CENTER ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), + $text['position']->y + $yOffset + ); + break; + } + + // Optionally draw text shadow + if ( $text['font']->textShadow === true ) + { + $this->renderText( + $text['id'], + $string, + $chars[$text['font']->path], + $text['font']->type, + $text['font']->path, + $text['font']->textShadowColor, + new ezcGraphCoordinate( + $position->x + $this->modifyCoordinate( $text['font']->textShadowOffset ), + $position->y + $this->modifyCoordinate( $text['font']->textShadowOffset ) + ), + $size, + $text['rotation'] + ); + } + + // Finally draw text + $this->renderText( + $text['id'], + $string, + $chars[$text['font']->path], + $text['font']->type, + $text['font']->path, + $text['font']->color, + $position, + $size, + $text['rotation'] + ); + + $text['position']->y += $size * $this->options->lineSpacing; + } + } + } + + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center Center of circle + * @param mixed $width Width + * @param mixed $height Height + * @param mixed $startAngle Start angle of circle sector + * @param mixed $endAngle End angle of circle sector + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + $movie = $this->getDocument(); + + $shape = new SWFShape(); + $this->setShapeColor( $shape, $color, 1, $filled ); + + if ( !$filled ) + { + try + { + $reduced = $this->reduceEllipseSize( $center, $width, $height, $startAngle, $endAngle, .5 ); + } + catch ( ezcGraphReducementFailedException $e ) + { + return false; + } + + $startAngle = $reduced['startAngle']; + $endAngle = $reduced['endAngle']; + + $width -= 1; + $height -= 1; + } + + $shape->movePenTo( $this->modifyCoordinate( $center->x ), $this->modifyCoordinate( $center->y ) ); + + // @TODO: User SWFShape::curveTo + for( + $angle = $startAngle; + $angle <= $endAngle; + $angle = min( $angle + $this->options->circleResolution, $endAngle ) ) + { + $shape->drawLineTo( + $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), + $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) + ); + + if ( $angle === $endAngle ) + { + break; + } + } + + $shape->drawLineTo( + $this->modifyCoordinate( $center->x ), + $this->modifyCoordinate( $center->y ) + ); + + $object = $movie->add( $shape ); + $object->setName( $id = 'ezcGraphCircleSector_' . $this->id++ ); + + return $id; + } + + /** + * Draws a circular arc consisting of several minor steps on the bounding + * lines. + * + * @param ezcGraphCoordinate $center + * @param mixed $width + * @param mixed $height + * @param mixed $size + * @param mixed $startAngle + * @param mixed $endAngle + * @param ezcGraphColor $color + * @param bool $filled + * @return string Element id + */ + protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled ) + { + $movie = $this->getDocument(); + $id = 'ezcGraphCircularArc_' . $this->id++; + + for ( + $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle ); + $tmpAngle <= $endAngle; + $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) ) + { + $shape = new SWFShape(); + $this->setShapeColor( $shape, $color, 1, $filled ); + + $shape->movePenTo( + $this->modifyCoordinate( $center->x + cos( deg2rad( $startAngle ) ) * $width / 2 ), + $this->modifyCoordinate( $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 ) + ); + + // @TODO: Use SWFShape::curveTo + for( + $angle = $startAngle; + $angle <= $tmpAngle; + $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) ) + { + $shape->drawLineTo( + $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), + $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size ) + ); + + if ( $angle === $tmpAngle ) + { + break; + } + } + + for( + $angle = $tmpAngle; + $angle >= $startAngle; + $angle = max( $angle - $this->options->circleResolution, $startAngle ) ) + { + $shape->drawLineTo( + $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), + $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) + ); + + if ( $angle === $startAngle ) + { + break; + } + } + + $object = $movie->add( $shape ); + $object->setName( $id ); + + $startAngle = $tmpAngle; + if ( $tmpAngle === $endAngle ) + { + break; + } + } + + return $id; + } + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled + * @return void + */ + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + $id = $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled ); + + if ( ( $this->options->shadeCircularArc !== false ) && + $filled ) + { + $gradient = new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $center->x - $width, + $center->y + ), + new ezcGraphCoordinate( + $center->x + $width, + $center->y + ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), + ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 ) + ); + + $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled ); + } + + return $id; + } + + /** + * Draw circle + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param mixed $width Width of ellipse + * @param mixed $height height of ellipse + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) + { + $movie = $this->getDocument(); + + $shape = new SWFShape(); + $this->setShapeColor( $shape, $color, 1, $filled ); + + // Reduce size + if ( !$filled ) + { + $width -= 1; + $height -= 1; + } + + $shape->movePenTo( + $this->modifyCoordinate( $center->x + $width / 2 ), + $this->modifyCoordinate( $center->y ) + ); + + // @TODO: User SWFShape::curveTo + for ( $angle = $this->options->circleResolution; $angle < 360; $angle += $this->options->circleResolution ) + { + $shape->drawLineTo( + $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ), + $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 ) + ); + } + + $shape->drawLineTo( + $this->modifyCoordinate( $center->x + $width / 2 ), + $this->modifyCoordinate( $center->y ) + ); + + $object = $movie->add( $shape ); + $object->setName( $id = 'ezcGraphCircle_' . $this->id++ ); + + return $id; + } + + /** + * Draw an image + * + * The image will be inlined in the SVG document using data URL scheme. For + * this the mime type and base64 encoded file content will be merged to + * URL. + * + * @param mixed $file Image file + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of image in destination image + * @param float $height Height of image in destination image + * @return void + */ + public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) + { + $movie = $this->getDocument(); + + $imageData = getimagesize( $file ); + if ( ( $imageData[2] !== IMAGETYPE_JPEG ) && ( $imageData[2] !== IMAGETYPE_PNG ) ) + { + throw new ezcGraphFlashBitmapTypeException( $imageData[2] ); + } + + // Try to create a new SWFBitmap object from provided file + $bitmap = new SWFBitmap( fopen( $file, 'rb' ) ); + + // Add the image to the movie + $object = $this->movie->add( $bitmap ); + + // Image size is calculated on the base of a tick size of 20, so + // that we need to transform this, to our tick size. + $factor = $this->modifyCoordinate( 1 ) / 20; + $object->scale( $factor, $factor ); + + // Scale by ratio of requested and original image size + $object->scale( + $width / $imageData[0], + $height / $imageData[1] + ); + + // Move object to the right position + $object->moveTo( + $this->modifyCoordinate( $position->x ), + $this->modifyCoordinate( $position->y ) + ); + + // Create, set and return unique ID + $object->setName( $id = 'ezcGraphImage_'. $this->id++ ); + return $id; + } + + /** + * Return mime type for current image format + * + * @return string + */ + public function getMimeType() + { + return 'application/x-shockwave-flash'; + } + + /** + * Finally save image + * + * @param string $file Destination filename + * @return void + */ + public function render( $file ) + { + $this->drawAllTexts(); + $movie = $this->getDocument(); + $movie->save( $file, $this->options->compression ); + } + + /** + * Get resource of rendered result + * + * Return the resource of the rendered result. You should not use this + * method before you called either renderToOutput() or render(), as the + * image may not be completely rendered until then. + * + * @return SWFMovie + */ + public function getResource() + { + return $this->movie; + } +} + +?> diff --git a/library/ezc/Graph/src/driver/gd.php b/library/ezc/Graph/src/driver/gd.php index ef3fbda9c1..b1cca35218 100644 --- a/library/ezc/Graph/src/driver/gd.php +++ b/library/ezc/Graph/src/driver/gd.php @@ -1,1236 +1,1236 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzGreen(); - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->driver = new ezcGraphGdDriver(); - * $graph->options->font = 'tutorial_font.ttf'; - * - * // Generate a Jpeg with lower quality. The default settings result in a image - * // with better quality. - * // - * // The reduction of the supersampling to 1 will result in no anti aliasing of - * // the image. JPEG is not the optimal format for grapics, PNG is far better for - * // this kind of images. - * $graph->driver->options->supersampling = 1; - * $graph->driver->options->jpegQuality = 100; - * $graph->driver->options->imageFormat = IMG_JPEG; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' ); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphGdDriver extends ezcGraphDriver -{ - - /** - * Image resource - * - * @var resource - */ - protected $image; - - /** - * Array with image files to draw - * - * @var array - */ - protected $preProcessImages = array(); - - /** - * List of strings to draw - * array ( array( - * 'text' => array( 'strings' ), - * 'options' => ezcGraphFontOptions, - * ) - * - * @var array - */ - protected $strings = array(); - - /** - * Contains resources for already loaded ps fonts. - * array( - * path => resource - * ) - * - * @var array - */ - protected $psFontResources = array(); - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'gd' ); - $this->options = new ezcGraphGdDriverOptions( $options ); - } - - /** - * Returns the image resource to draw on. - * - * If no resource exists the image will be created. The size of the - * returned image depends on the supersampling factor and the size of the - * chart. - * - * @return resource - */ - protected function getImage() - { - if ( !isset( $this->image ) ) - { - $this->image = imagecreatetruecolor( - $this->supersample( $this->options->width ), - $this->supersample( $this->options->height ) - ); - - // Default to a transparent white background - $bgColor = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 ); - imagealphablending( $this->image, true ); - imagesavealpha( $this->image, true ); - imagefill( $this->image, 1, 1, $bgColor ); - - imagesetthickness( - $this->image, - $this->options->supersampling - ); - } - - return $this->image; - } - - /** - * Allocates a color - * - * This function tries to allocate the requested color. If the color - * already exists in the imaga it will be reused. - * - * @param ezcGraphColor $color - * @return int Color index - */ - protected function allocate( ezcGraphColor $color ) - { - $image = $this->getImage(); - - if ( $color->alpha > 0 ) - { - $fetched = imagecolorexactalpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 ); - if ( $fetched < 0 ) - { - $fetched = imagecolorallocatealpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 ); - } - return $fetched; - } - else - { - $fetched = imagecolorexact( $image, $color->red, $color->green, $color->blue ); - if ( $fetched < 0 ) - { - $fetched = imagecolorallocate( $image, $color->red, $color->green, $color->blue ); - } - return $fetched; - } - } - - /** - * Creates an image resource from an image file - * - * @param string $file Filename - * @return resource Image - */ - protected function imageCreateFrom( $file ) - { - $data = getimagesize( $file ); - - switch ( $data[2] ) - { - case 1: - return array( - 'width' => $data[0], - 'height' => $data[1], - 'image' => imagecreatefromgif( $file ) - ); - case 2: - return array( - 'width' => $data[0], - 'height' => $data[1], - 'image' => imagecreatefromjpeg( $file ) - ); - case 3: - return array( - 'width' => $data[0], - 'height' => $data[1], - 'image' => imagecreatefrompng( $file ) - ); - default: - throw new ezcGraphGdDriverUnsupportedImageTypeException( $data[2] ); - } - } - - /** - * Supersamples a single coordinate value. - * - * Applies supersampling to a single coordinate value. - * - * @param float $value Coordinate value - * @return float Supersampled coordinate value - */ - protected function supersample( $value ) - { - $mod = (int) floor( $this->options->supersampling / 2 ); - return $value * $this->options->supersampling - $mod; - } - - /** - * Draws a single polygon. - * - * @param array $points Point array - * @param ezcGraphColor $color Polygon color - * @param mixed $filled Filled - * @param float $thickness Line thickness - * @return void - */ - public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - $image = $this->getImage(); - - $drawColor = $this->allocate( $color ); - - // Create point array - $pointCount = count( $points ); - $pointArray = array(); - for ( $i = 0; $i < $pointCount; ++$i ) - { - $pointArray[] = $this->supersample( $points[$i]->x ); - $pointArray[] = $this->supersample( $points[$i]->y ); - } - - // Draw polygon - if ( $filled ) - { - imagefilledpolygon( $image, $pointArray, $pointCount, $drawColor ); - } - else - { - imagepolygon( $image, $pointArray, $pointCount, $drawColor ); - } - - return $points; - } - - /** - * Draws a line - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Line color - * @param float $thickness Line thickness - * @return void - */ - public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) - { - $image = $this->getImage(); - - $drawColor = $this->allocate( $color ); - - imagesetthickness( - $this->image, - $this->options->supersampling * $thickness - ); - - imageline( - $image, - $this->supersample( $start->x ), - $this->supersample( $start->y ), - $this->supersample( $end->x ), - $this->supersample( $end->y ), - $drawColor - ); - - imagesetthickness( - $this->image, - $this->options->supersampling - ); - - return array(); - } - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) - { - switch ( $font->type ) - { - case ezcGraph::PS_FONT: - if ( !isset( $this->psFontResources[$font->path] ) ) - { - $this->psFontResources[$font->path] = imagePsLoadFont( $font->path ); - } - - $boundings = imagePsBBox( $text, $this->psFontResources[$font->path], $size ); - return new ezcGraphBoundings( - $boundings[0], - $boundings[1], - $boundings[2], - $boundings[3] - ); - case ezcGraph::TTF_FONT: - switch ( true ) - { - case ezcBaseFeatures::hasFunction( 'imageftbbox' ) && !$this->options->forceNativeTTF: - $boundings = imageFtBBox( $size, 0, $font->path, $text ); - return new ezcGraphBoundings( - $boundings[0], - $boundings[1], - $boundings[4], - $boundings[5] - ); - case ezcBaseFeatures::hasFunction( 'imagettfbbox' ): - $boundings = imageTtfBBox( $size, 0, $font->path, $text ); - return new ezcGraphBoundings( - $boundings[0], - $boundings[1], - $boundings[4], - $boundings[5] - ); - } - break; - } - } - - /** - * Render text depending of font type and available font extensions - * - * @param resource $image Image resource - * @param string $text Text - * @param int $type Font type - * @param string $path Font path - * @param ezcGraphColor $color Font color - * @param ezcGraphCoordinate $position Position - * @param float $size Textsize - * @param ezcGraphRotation $rotation - * - * @return void - */ - protected function renderText( $image, $text, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, ezcGraphRotation $rotation = null ) - { - if ( $rotation !== null ) - { - // Rotation is relative to top left point of text and not relative - // to the bounding coordinate system - $rotation = new ezcGraphRotation( - $rotation->getRotation(), - new ezcGraphCoordinate( - $rotation->getCenter()->x - $position->x, - $rotation->getCenter()->y - $position->y - ) - ); - } - - switch ( $type ) - { - case ezcGraph::PS_FONT: - imagePsText( - $image, - $text, - $this->psFontResources[$path], - $size, - $this->allocate( $color ), - 1, - $position->x + - ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), - $position->y + - ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), - 0, - 0, - ( $rotation === null ? 0 : -$rotation->getRotation() ), - 4 - ); - break; - case ezcGraph::TTF_FONT: - switch ( true ) - { - case ezcBaseFeatures::hasFunction( 'imagefttext' ) && !$this->options->forceNativeTTF: - imageFtText( - $image, - $size, - ( $rotation === null ? 0 : -$rotation->getRotation() ), - $position->x + - ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), - $position->y + - ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), - $this->allocate( $color ), - $path, - $text - ); - break; - case ezcBaseFeatures::hasFunction( 'imagettftext' ): - imageTtfText( - $image, - $size, - ( $rotation === null ? 0 : -$rotation->getRotation() ), - $position->x + - ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), - $position->y + - ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), - $this->allocate( $color ), - $path, - $text - ); - break; - } - break; - } - } - - /** - * Writes text in a box of desired size - * - * @param string $string Text - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of text box - * @param float $height Height of text box - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) - { - $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); - - $width -= $padding * 2; - $height -= $padding * 2; - $position->x += $padding; - $position->y += $padding; - - // Try to get a font size for the text to fit into the box - $maxSize = min( $height, $this->options->font->maxFontSize ); - $result = false; - for ( $size = $maxSize; $size >= $this->options->font->minFontSize; --$size ) - { - $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); - if ( is_array( $result ) ) - { - break; - } - $size = floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ); - } - - if ( !is_array( $result ) ) - { - if ( ( $height >= $this->options->font->minFontSize ) && - ( $this->options->autoShortenString ) ) - { - $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); - } - else - { - throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); - } - } - - $this->options->font->minimalUsedFont = $size; - - $this->strings[] = array( - 'text' => $result, - 'position' => $position, - 'width' => $width, - 'height' => $height, - 'align' => $align, - 'font' => $this->options->font, - 'rotation' => $rotation, - ); - - return array( - clone $position, - new ezcGraphCoordinate( $position->x + $width, $position->y ), - new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), - new ezcGraphCoordinate( $position->x, $position->y + $height ), - ); - } - - /** - * Draw all collected texts - * - * The texts are collected and their maximum possible font size is - * calculated. This function finally draws the texts on the image, this - * delayed drawing has two reasons: - * - * 1) This way the text strings are always on top of the image, what - * results in better readable texts - * 2) The maximum possible font size can be calculated for a set of texts - * with the same font configuration. Strings belonging to one chart - * element normally have the same font configuration, so that all texts - * belonging to one element will have the same font size. - * - * @access protected - * @return void - */ - protected function drawAllTexts() - { - $image = $this->getImage(); - - foreach ( $this->strings as $text ) - { - $size = $text['font']->minimalUsedFont; - - $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; - - // Calculate y offset for vertical alignement - switch ( true ) - { - case ( $text['align'] & ezcGraph::BOTTOM ): - $yOffset = $text['height'] - $completeHeight; - break; - case ( $text['align'] & ezcGraph::MIDDLE ): - $yOffset = ( $text['height'] - $completeHeight ) / 2; - break; - case ( $text['align'] & ezcGraph::TOP ): - default: - $yOffset = 0; - break; - } - - $padding = $text['font']->padding + $text['font']->borderWidth / 2; - if ( $this->options->font->minimizeBorder === true ) - { - // Calculate maximum width of text rows - $width = false; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - if ( ( $width === false) || ( $boundings->width > $width ) ) - { - $width = $boundings->width; - } - } - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $xOffset = 0; - break; - case ( $text['align'] & ezcGraph::CENTER ): - $xOffset = ( $text['width'] - $width ) / 2; - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $xOffset = $text['width'] - $width; - break; - } - - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - ); - } - else - { - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y + $padding * 2 + $text['height'] - ), - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y + $padding * 2 + $text['height'] - ), - ); - } - - if ( $text['rotation'] !== null ) - { - foreach ( $borderPolygonArray as $nr => $point ) - { - $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); - } - } - - if ( $text['font']->background !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->background, - true - ); - } - - if ( $text['font']->border !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->border, - false, - $text['font']->borderWidth - ); - } - - // Render text with evaluated font size - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - $boundings = $this->getTextBoundings( $size, $text['font'], $string ); - $text['position']->y += $size; - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $position = new ezcGraphCoordinate( - $text['position']->x, - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( $text['width'] - $boundings->width ), - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::CENTER ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), - $text['position']->y + $yOffset - ); - break; - } - - // Calculate relative modification of rotation center point - if ( $text['rotation'] !== null ) - { - $rotation = new ezcGraphRotation( - $text['rotation']->getRotation(), - new ezcGraphCoordinate( - $text['rotation']->getCenter()->x + - $position->x - $text['position']->x, - $text['rotation']->getCenter()->y + - $position->y - $text['position']->y - ) - ); - $rotation = $text['rotation']; - } - else - { - $rotation = null; - } - - // Optionally draw text shadow - if ( $text['font']->textShadow === true ) - { - $this->renderText( - $image, - $string, - $text['font']->type, - $text['font']->path, - $text['font']->textShadowColor, - new ezcGraphCoordinate( - $position->x + $text['font']->textShadowOffset, - $position->y + $text['font']->textShadowOffset - ), - $size, - $rotation - ); - } - - // Finally draw text - $this->renderText( - $image, - $string, - $text['font']->type, - $text['font']->path, - $text['font']->color, - $position, - $size, - $rotation - ); - - $text['position']->y += $size * $this->options->lineSpacing; - } - } - } - - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center Center of circle - * @param mixed $width Width - * @param mixed $height Height - * @param mixed $startAngle Start angle of circle sector - * @param mixed $endAngle End angle of circle sector - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $image = $this->getImage(); - $drawColor = $this->allocate( $color ); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - if ( ( $endAngle - $startAngle ) > 359.99999 ) - { - return $this->drawCircle( $center, $width, $height, $color, $filled ); - } - - // Because of bug #45552 in PHPs ext/GD we check for a minimal distance - // on the outer border of the circle sector, and skip the drawing if - // the distance is lower then 1. - // - // See also: http://bugs.php.net/45552 - $startPoint = new ezcGraphVector( - $center->x + - ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) - ); - if ( $startPoint->sub( new ezcGraphVector( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ) )->length() < 1 ) - { - // Skip this circle sector - return array(); - } - - if ( $filled ) - { - imagefilledarc( - $image, - $this->supersample( $center->x ), - $this->supersample( $center->y ), - $this->supersample( $width ), - $this->supersample( $height ), - $startAngle, - $endAngle, - $drawColor, - IMG_ARC_PIE - ); - } - else - { - imagefilledarc( - $image, - $this->supersample( $center->x ), - $this->supersample( $center->y ), - $this->supersample( $width ), - $this->supersample( $height ), - $startAngle, - $endAngle, - $drawColor, - IMG_ARC_PIE | IMG_ARC_NOFILL | IMG_ARC_EDGED - ); - } - - // Create polygon array to return - $polygonArray = array( $center ); - for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ); - - return $polygonArray; - } - - /** - * Draws a single element of a circular arc - * - * ext/gd itself does not support something like circular arcs, so that - * this functions draws rectangular polygons as a part of circular arcs - * to interpolate them. This way it is possible to apply a linear gradient - * to the circular arc, because we draw single steps anyway. - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @return void - */ - protected function drawCircularArcStep( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color ) - { - $this->drawPolygon( - array( - new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) - ), - new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + $size - ), - new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + $size - ), - new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ), - ), - $color->darken( $this->options->shadeCircularArc * ( 1 + cos ( deg2rad( $startAngle ) ) ) / 2 ), - true - ); - } - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled - * @return void - */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $image = $this->getImage(); - $drawColor = $this->allocate( $color ); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - if ( $filled === true ) - { - $startIteration = ceil( $startAngle / $this->options->detail ) * $this->options->detail; - $endIteration = floor( $endAngle / $this->options->detail ) * $this->options->detail; - - if ( $startAngle < $startIteration ) - { - // Draw initial step - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $startAngle, - $startIteration, - $color - ); - } - - // Draw all steps - for ( ; $startIteration < $endIteration; $startIteration += $this->options->detail ) - { - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $startIteration, - $startIteration + $this->options->detail, - $color - ); - } - - if ( $endIteration < $endAngle ) - { - // Draw closing step - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $endIteration, - $endAngle, - $color - ); - } - } - else - { - imagefilledarc( - $image, - $this->supersample( $center->x ), - $this->supersample( $center->y ), - $this->supersample( $width ), - $this->supersample( $height ), - $startAngle, - $endAngle, - $drawColor, - IMG_ARC_PIE | IMG_ARC_NOFILL - ); - } - - // Create polygon array to return - $polygonArray = array(); - for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) - ); - - for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size, - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size, - $center->y + - ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) - ); - - return $polygonArray; - } - - /** - * Draw circle - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param mixed $width Width of ellipse - * @param mixed $height height of ellipse - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) - { - $image = $this->getImage(); - - $drawColor = $this->allocate( $color ); - - if ( $filled ) - { - imagefilledellipse( - $image, - $this->supersample( $center->x ), - $this->supersample( $center->y ), - $this->supersample( $width ), - $this->supersample( $height ), - $drawColor - ); - } - else - { - imageellipse( - $image, - $this->supersample( $center->x ), - $this->supersample( $center->y ), - $this->supersample( $width ), - $this->supersample( $height ), - $drawColor - ); - } - - $polygonArray = array(); - for ( $angle = 0; $angle < 360; $angle += $this->options->imageMapResolution ) - { - $polygonArray[] = new ezcGraphCoordinate( - $center->x + - ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), - $center->y + - ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) - ); - } - - return $polygonArray; - } - - /** - * Draw an image - * - * The actual drawing of the image is delayed, to not apply supersampling - * to the image. The image will normally be resized using the gd function - * imagecopyresampled, which provides nice antialiased scaling, so that - * additional supersampling would make the image look blurred. The delayed - * images will be pre-processed, so that they are draw in the back of - * everything else. - * - * @param mixed $file Image file - * @param ezcGraphCoordinate $position Top left position - * @param mixed $width Width of image in destination image - * @param mixed $height Height of image in destination image - * @return void - */ - public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) - { - $this->preProcessImages[] = array( - 'file' => $file, - 'position' => clone $position, - 'width' => $width, - 'height' => $height, - ); - - return array( - $position, - new ezcGraphCoordinate( $position->x + $width, $position->y ), - new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), - new ezcGraphCoordinate( $position->x, $position->y + $height ), - ); - } - - /** - * Draw all images to image resource handler - * - * @param resource $image Image to draw on - * @return resource Updated image resource - */ - protected function addImages( $image ) - { - foreach ( $this->preProcessImages as $preImage ) - { - $preImageData = $this->imageCreateFrom( $preImage['file'] ); - call_user_func_array( - $this->options->resampleFunction, - array( - $image, - $preImageData['image'], - $preImage['position']->x, $preImage['position']->y, - 0, 0, - $preImage['width'], $preImage['height'], - $preImageData['width'], $preImageData['height'], - ) - ); - } - - return $image; - } - - /** - * Return mime type for current image format - * - * @return string - */ - public function getMimeType() - { - switch ( $this->options->imageFormat ) - { - case IMG_PNG: - return 'image/png'; - case IMG_JPEG: - return 'image/jpeg'; - } - } - - /** - * Render image directly to output - * - * The method renders the image directly to the standard output. You - * normally do not want to use this function, because it makes it harder - * to proper cache the generated graphs. - * - * @return void - */ - public function renderToOutput() - { - header( 'Content-Type: ' . $this->getMimeType() ); - $this->render( null ); - } - - /** - * Finally save image - * - * @param string $file Destination filename - * @return void - */ - public function render( $file ) - { - $destination = imagecreatetruecolor( $this->options->width, $this->options->height ); - - // Default to a transparent white background - $bgColor = imagecolorallocatealpha( $destination, 255, 255, 255, 127 ); - imagealphablending( $destination, true ); - imagesavealpha( $destination, true ); - imagefill( $destination, 1, 1, $bgColor ); - - // Apply background if one is defined - if ( $this->options->background !== false ) - { - $background = $this->imageCreateFrom( $this->options->background ); - - call_user_func_array( - $this->options->resampleFunction, - array( - $destination, - $background['image'], - 0, 0, - 0, 0, - $this->options->width, $this->options->height, - $background['width'], $background['height'], - ) - ); - } - - // Draw all images to exclude them from supersampling - $destination = $this->addImages( $destination ); - - // Finally merge with graph - $image = $this->getImage(); - call_user_func_array( - $this->options->resampleFunction, - array( - $destination, - $image, - 0, 0, - 0, 0, - $this->options->width, $this->options->height, - $this->supersample( $this->options->width ), $this->supersample( $this->options->height ) - ) - ); - - $this->image = $destination; - imagedestroy( $image ); - - // Draw all texts - // Reset supersampling during text rendering - $supersampling = $this->options->supersampling; - $this->options->supersampling = 1; - $this->drawAllTexts(); - $this->options->supersampling = $supersampling; - - $image = $this->getImage(); - switch ( $this->options->imageFormat ) - { - case IMG_PNG: - if ( $file === null ) - { - imagepng( $image ); - } - else - { - imagepng( $image, $file ); - } - break; - case IMG_JPEG: - imagejpeg( $image, $file, $this->options->jpegQuality ); - break; - default: - throw new ezcGraphGdDriverUnsupportedImageTypeException( $this->options->imageFormat ); - } - } - - /** - * Get resource of rendered result - * - * Return the resource of the rendered result. You should not use this - * method before you called either renderToOutput() or render(), as the - * image may not be completely rendered until then. - * - * @return resource - */ - public function getResource() - { - return $this->image; - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzGreen(); + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->driver = new ezcGraphGdDriver(); + * $graph->options->font = 'tutorial_font.ttf'; + * + * // Generate a Jpeg with lower quality. The default settings result in a image + * // with better quality. + * // + * // The reduction of the supersampling to 1 will result in no anti aliasing of + * // the image. JPEG is not the optimal format for grapics, PNG is far better for + * // this kind of images. + * $graph->driver->options->supersampling = 1; + * $graph->driver->options->jpegQuality = 100; + * $graph->driver->options->imageFormat = IMG_JPEG; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' ); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphGdDriver extends ezcGraphDriver +{ + + /** + * Image resource + * + * @var resource + */ + protected $image; + + /** + * Array with image files to draw + * + * @var array + */ + protected $preProcessImages = array(); + + /** + * List of strings to draw + * array ( array( + * 'text' => array( 'strings' ), + * 'options' => ezcGraphFontOptions, + * ) + * + * @var array + */ + protected $strings = array(); + + /** + * Contains resources for already loaded ps fonts. + * array( + * path => resource + * ) + * + * @var array + */ + protected $psFontResources = array(); + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'gd' ); + $this->options = new ezcGraphGdDriverOptions( $options ); + } + + /** + * Returns the image resource to draw on. + * + * If no resource exists the image will be created. The size of the + * returned image depends on the supersampling factor and the size of the + * chart. + * + * @return resource + */ + protected function getImage() + { + if ( !isset( $this->image ) ) + { + $this->image = imagecreatetruecolor( + $this->supersample( $this->options->width ), + $this->supersample( $this->options->height ) + ); + + // Default to a transparent white background + $bgColor = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 ); + imagealphablending( $this->image, true ); + imagesavealpha( $this->image, true ); + imagefill( $this->image, 1, 1, $bgColor ); + + imagesetthickness( + $this->image, + $this->options->supersampling + ); + } + + return $this->image; + } + + /** + * Allocates a color + * + * This function tries to allocate the requested color. If the color + * already exists in the imaga it will be reused. + * + * @param ezcGraphColor $color + * @return int Color index + */ + protected function allocate( ezcGraphColor $color ) + { + $image = $this->getImage(); + + if ( $color->alpha > 0 ) + { + $fetched = imagecolorexactalpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 ); + if ( $fetched < 0 ) + { + $fetched = imagecolorallocatealpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 ); + } + return $fetched; + } + else + { + $fetched = imagecolorexact( $image, $color->red, $color->green, $color->blue ); + if ( $fetched < 0 ) + { + $fetched = imagecolorallocate( $image, $color->red, $color->green, $color->blue ); + } + return $fetched; + } + } + + /** + * Creates an image resource from an image file + * + * @param string $file Filename + * @return resource Image + */ + protected function imageCreateFrom( $file ) + { + $data = getimagesize( $file ); + + switch ( $data[2] ) + { + case 1: + return array( + 'width' => $data[0], + 'height' => $data[1], + 'image' => imagecreatefromgif( $file ) + ); + case 2: + return array( + 'width' => $data[0], + 'height' => $data[1], + 'image' => imagecreatefromjpeg( $file ) + ); + case 3: + return array( + 'width' => $data[0], + 'height' => $data[1], + 'image' => imagecreatefrompng( $file ) + ); + default: + throw new ezcGraphGdDriverUnsupportedImageTypeException( $data[2] ); + } + } + + /** + * Supersamples a single coordinate value. + * + * Applies supersampling to a single coordinate value. + * + * @param float $value Coordinate value + * @return float Supersampled coordinate value + */ + protected function supersample( $value ) + { + $mod = (int) floor( $this->options->supersampling / 2 ); + return $value * $this->options->supersampling - $mod; + } + + /** + * Draws a single polygon. + * + * @param array $points Point array + * @param ezcGraphColor $color Polygon color + * @param mixed $filled Filled + * @param float $thickness Line thickness + * @return void + */ + public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + $image = $this->getImage(); + + $drawColor = $this->allocate( $color ); + + // Create point array + $pointCount = count( $points ); + $pointArray = array(); + for ( $i = 0; $i < $pointCount; ++$i ) + { + $pointArray[] = $this->supersample( $points[$i]->x ); + $pointArray[] = $this->supersample( $points[$i]->y ); + } + + // Draw polygon + if ( $filled ) + { + imagefilledpolygon( $image, $pointArray, $pointCount, $drawColor ); + } + else + { + imagepolygon( $image, $pointArray, $pointCount, $drawColor ); + } + + return $points; + } + + /** + * Draws a line + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Line color + * @param float $thickness Line thickness + * @return void + */ + public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) + { + $image = $this->getImage(); + + $drawColor = $this->allocate( $color ); + + imagesetthickness( + $this->image, + $this->options->supersampling * $thickness + ); + + imageline( + $image, + $this->supersample( $start->x ), + $this->supersample( $start->y ), + $this->supersample( $end->x ), + $this->supersample( $end->y ), + $drawColor + ); + + imagesetthickness( + $this->image, + $this->options->supersampling + ); + + return array(); + } + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) + { + switch ( $font->type ) + { + case ezcGraph::PS_FONT: + if ( !isset( $this->psFontResources[$font->path] ) ) + { + $this->psFontResources[$font->path] = imagePsLoadFont( $font->path ); + } + + $boundings = imagePsBBox( $text, $this->psFontResources[$font->path], $size ); + return new ezcGraphBoundings( + $boundings[0], + $boundings[1], + $boundings[2], + $boundings[3] + ); + case ezcGraph::TTF_FONT: + switch ( true ) + { + case ezcBaseFeatures::hasFunction( 'imageftbbox' ) && !$this->options->forceNativeTTF: + $boundings = imageFtBBox( $size, 0, $font->path, $text ); + return new ezcGraphBoundings( + $boundings[0], + $boundings[1], + $boundings[4], + $boundings[5] + ); + case ezcBaseFeatures::hasFunction( 'imagettfbbox' ): + $boundings = imageTtfBBox( $size, 0, $font->path, $text ); + return new ezcGraphBoundings( + $boundings[0], + $boundings[1], + $boundings[4], + $boundings[5] + ); + } + break; + } + } + + /** + * Render text depending of font type and available font extensions + * + * @param resource $image Image resource + * @param string $text Text + * @param int $type Font type + * @param string $path Font path + * @param ezcGraphColor $color Font color + * @param ezcGraphCoordinate $position Position + * @param float $size Textsize + * @param ezcGraphRotation $rotation + * + * @return void + */ + protected function renderText( $image, $text, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, ezcGraphRotation $rotation = null ) + { + if ( $rotation !== null ) + { + // Rotation is relative to top left point of text and not relative + // to the bounding coordinate system + $rotation = new ezcGraphRotation( + $rotation->getRotation(), + new ezcGraphCoordinate( + $rotation->getCenter()->x - $position->x, + $rotation->getCenter()->y - $position->y + ) + ); + } + + switch ( $type ) + { + case ezcGraph::PS_FONT: + imagePsText( + $image, + $text, + $this->psFontResources[$path], + $size, + $this->allocate( $color ), + 1, + $position->x + + ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), + $position->y + + ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), + 0, + 0, + ( $rotation === null ? 0 : -$rotation->getRotation() ), + 4 + ); + break; + case ezcGraph::TTF_FONT: + switch ( true ) + { + case ezcBaseFeatures::hasFunction( 'imagefttext' ) && !$this->options->forceNativeTTF: + imageFtText( + $image, + $size, + ( $rotation === null ? 0 : -$rotation->getRotation() ), + $position->x + + ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), + $position->y + + ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), + $this->allocate( $color ), + $path, + $text + ); + break; + case ezcBaseFeatures::hasFunction( 'imagettftext' ): + imageTtfText( + $image, + $size, + ( $rotation === null ? 0 : -$rotation->getRotation() ), + $position->x + + ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ), + $position->y + + ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ), + $this->allocate( $color ), + $path, + $text + ); + break; + } + break; + } + } + + /** + * Writes text in a box of desired size + * + * @param string $string Text + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of text box + * @param float $height Height of text box + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) + { + $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); + + $width -= $padding * 2; + $height -= $padding * 2; + $position->x += $padding; + $position->y += $padding; + + // Try to get a font size for the text to fit into the box + $maxSize = min( $height, $this->options->font->maxFontSize ); + $result = false; + for ( $size = $maxSize; $size >= $this->options->font->minFontSize; --$size ) + { + $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); + if ( is_array( $result ) ) + { + break; + } + $size = floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ); + } + + if ( !is_array( $result ) ) + { + if ( ( $height >= $this->options->font->minFontSize ) && + ( $this->options->autoShortenString ) ) + { + $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); + } + else + { + throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); + } + } + + $this->options->font->minimalUsedFont = $size; + + $this->strings[] = array( + 'text' => $result, + 'position' => $position, + 'width' => $width, + 'height' => $height, + 'align' => $align, + 'font' => $this->options->font, + 'rotation' => $rotation, + ); + + return array( + clone $position, + new ezcGraphCoordinate( $position->x + $width, $position->y ), + new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), + new ezcGraphCoordinate( $position->x, $position->y + $height ), + ); + } + + /** + * Draw all collected texts + * + * The texts are collected and their maximum possible font size is + * calculated. This function finally draws the texts on the image, this + * delayed drawing has two reasons: + * + * 1) This way the text strings are always on top of the image, what + * results in better readable texts + * 2) The maximum possible font size can be calculated for a set of texts + * with the same font configuration. Strings belonging to one chart + * element normally have the same font configuration, so that all texts + * belonging to one element will have the same font size. + * + * @access protected + * @return void + */ + protected function drawAllTexts() + { + $image = $this->getImage(); + + foreach ( $this->strings as $text ) + { + $size = $text['font']->minimalUsedFont; + + $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; + + // Calculate y offset for vertical alignement + switch ( true ) + { + case ( $text['align'] & ezcGraph::BOTTOM ): + $yOffset = $text['height'] - $completeHeight; + break; + case ( $text['align'] & ezcGraph::MIDDLE ): + $yOffset = ( $text['height'] - $completeHeight ) / 2; + break; + case ( $text['align'] & ezcGraph::TOP ): + default: + $yOffset = 0; + break; + } + + $padding = $text['font']->padding + $text['font']->borderWidth / 2; + if ( $this->options->font->minimizeBorder === true ) + { + // Calculate maximum width of text rows + $width = false; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + if ( ( $width === false) || ( $boundings->width > $width ) ) + { + $width = $boundings->width; + } + } + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $xOffset = 0; + break; + case ( $text['align'] & ezcGraph::CENTER ): + $xOffset = ( $text['width'] - $width ) / 2; + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $xOffset = $text['width'] - $width; + break; + } + + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + ); + } + else + { + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y + $padding * 2 + $text['height'] + ), + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y + $padding * 2 + $text['height'] + ), + ); + } + + if ( $text['rotation'] !== null ) + { + foreach ( $borderPolygonArray as $nr => $point ) + { + $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point ); + } + } + + if ( $text['font']->background !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->background, + true + ); + } + + if ( $text['font']->border !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->border, + false, + $text['font']->borderWidth + ); + } + + // Render text with evaluated font size + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + $boundings = $this->getTextBoundings( $size, $text['font'], $string ); + $text['position']->y += $size; + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $position = new ezcGraphCoordinate( + $text['position']->x, + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( $text['width'] - $boundings->width ), + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::CENTER ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), + $text['position']->y + $yOffset + ); + break; + } + + // Calculate relative modification of rotation center point + if ( $text['rotation'] !== null ) + { + $rotation = new ezcGraphRotation( + $text['rotation']->getRotation(), + new ezcGraphCoordinate( + $text['rotation']->getCenter()->x + + $position->x - $text['position']->x, + $text['rotation']->getCenter()->y + + $position->y - $text['position']->y + ) + ); + $rotation = $text['rotation']; + } + else + { + $rotation = null; + } + + // Optionally draw text shadow + if ( $text['font']->textShadow === true ) + { + $this->renderText( + $image, + $string, + $text['font']->type, + $text['font']->path, + $text['font']->textShadowColor, + new ezcGraphCoordinate( + $position->x + $text['font']->textShadowOffset, + $position->y + $text['font']->textShadowOffset + ), + $size, + $rotation + ); + } + + // Finally draw text + $this->renderText( + $image, + $string, + $text['font']->type, + $text['font']->path, + $text['font']->color, + $position, + $size, + $rotation + ); + + $text['position']->y += $size * $this->options->lineSpacing; + } + } + } + + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center Center of circle + * @param mixed $width Width + * @param mixed $height Height + * @param mixed $startAngle Start angle of circle sector + * @param mixed $endAngle End angle of circle sector + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $image = $this->getImage(); + $drawColor = $this->allocate( $color ); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + if ( ( $endAngle - $startAngle ) > 359.99999 ) + { + return $this->drawCircle( $center, $width, $height, $color, $filled ); + } + + // Because of bug #45552 in PHPs ext/GD we check for a minimal distance + // on the outer border of the circle sector, and skip the drawing if + // the distance is lower then 1. + // + // See also: http://bugs.php.net/45552 + $startPoint = new ezcGraphVector( + $center->x + + ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + ); + if ( $startPoint->sub( new ezcGraphVector( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ) )->length() < 1 ) + { + // Skip this circle sector + return array(); + } + + if ( $filled ) + { + imagefilledarc( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $startAngle, + $endAngle, + $drawColor, + IMG_ARC_PIE + ); + } + else + { + imagefilledarc( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $startAngle, + $endAngle, + $drawColor, + IMG_ARC_PIE | IMG_ARC_NOFILL | IMG_ARC_EDGED + ); + } + + // Create polygon array to return + $polygonArray = array( $center ); + for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ); + + return $polygonArray; + } + + /** + * Draws a single element of a circular arc + * + * ext/gd itself does not support something like circular arcs, so that + * this functions draws rectangular polygons as a part of circular arcs + * to interpolate them. This way it is possible to apply a linear gradient + * to the circular arc, because we draw single steps anyway. + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @return void + */ + protected function drawCircularArcStep( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color ) + { + $this->drawPolygon( + array( + new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + ), + new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + $size + ), + new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + $size + ), + new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ), + ), + $color->darken( $this->options->shadeCircularArc * ( 1 + cos ( deg2rad( $startAngle ) ) ) / 2 ), + true + ); + } + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled + * @return void + */ + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $image = $this->getImage(); + $drawColor = $this->allocate( $color ); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + if ( $filled === true ) + { + $startIteration = ceil( $startAngle / $this->options->detail ) * $this->options->detail; + $endIteration = floor( $endAngle / $this->options->detail ) * $this->options->detail; + + if ( $startAngle < $startIteration ) + { + // Draw initial step + $this->drawCircularArcStep( + $center, + $width, + $height, + $size, + $startAngle, + $startIteration, + $color + ); + } + + // Draw all steps + for ( ; $startIteration < $endIteration; $startIteration += $this->options->detail ) + { + $this->drawCircularArcStep( + $center, + $width, + $height, + $size, + $startIteration, + $startIteration + $this->options->detail, + $color + ); + } + + if ( $endIteration < $endAngle ) + { + // Draw closing step + $this->drawCircularArcStep( + $center, + $width, + $height, + $size, + $endIteration, + $endAngle, + $color + ); + } + } + else + { + imagefilledarc( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $startAngle, + $endAngle, + $drawColor, + IMG_ARC_PIE | IMG_ARC_NOFILL + ); + } + + // Create polygon array to return + $polygonArray = array(); + for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + ); + + for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size, + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size, + $center->y + + ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + ); + + return $polygonArray; + } + + /** + * Draw circle + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param mixed $width Width of ellipse + * @param mixed $height height of ellipse + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) + { + $image = $this->getImage(); + + $drawColor = $this->allocate( $color ); + + if ( $filled ) + { + imagefilledellipse( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $drawColor + ); + } + else + { + imageellipse( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $drawColor + ); + } + + $polygonArray = array(); + for ( $angle = 0; $angle < 360; $angle += $this->options->imageMapResolution ) + { + $polygonArray[] = new ezcGraphCoordinate( + $center->x + + ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ), + $center->y + + ( ( sin( deg2rad( $angle ) ) * $height ) / 2 ) + ); + } + + return $polygonArray; + } + + /** + * Draw an image + * + * The actual drawing of the image is delayed, to not apply supersampling + * to the image. The image will normally be resized using the gd function + * imagecopyresampled, which provides nice antialiased scaling, so that + * additional supersampling would make the image look blurred. The delayed + * images will be pre-processed, so that they are draw in the back of + * everything else. + * + * @param mixed $file Image file + * @param ezcGraphCoordinate $position Top left position + * @param mixed $width Width of image in destination image + * @param mixed $height Height of image in destination image + * @return void + */ + public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) + { + $this->preProcessImages[] = array( + 'file' => $file, + 'position' => clone $position, + 'width' => $width, + 'height' => $height, + ); + + return array( + $position, + new ezcGraphCoordinate( $position->x + $width, $position->y ), + new ezcGraphCoordinate( $position->x + $width, $position->y + $height ), + new ezcGraphCoordinate( $position->x, $position->y + $height ), + ); + } + + /** + * Draw all images to image resource handler + * + * @param resource $image Image to draw on + * @return resource Updated image resource + */ + protected function addImages( $image ) + { + foreach ( $this->preProcessImages as $preImage ) + { + $preImageData = $this->imageCreateFrom( $preImage['file'] ); + call_user_func_array( + $this->options->resampleFunction, + array( + $image, + $preImageData['image'], + $preImage['position']->x, $preImage['position']->y, + 0, 0, + $preImage['width'], $preImage['height'], + $preImageData['width'], $preImageData['height'], + ) + ); + } + + return $image; + } + + /** + * Return mime type for current image format + * + * @return string + */ + public function getMimeType() + { + switch ( $this->options->imageFormat ) + { + case IMG_PNG: + return 'image/png'; + case IMG_JPEG: + return 'image/jpeg'; + } + } + + /** + * Render image directly to output + * + * The method renders the image directly to the standard output. You + * normally do not want to use this function, because it makes it harder + * to proper cache the generated graphs. + * + * @return void + */ + public function renderToOutput() + { + header( 'Content-Type: ' . $this->getMimeType() ); + $this->render( null ); + } + + /** + * Finally save image + * + * @param string $file Destination filename + * @return void + */ + public function render( $file ) + { + $destination = imagecreatetruecolor( $this->options->width, $this->options->height ); + + // Default to a transparent white background + $bgColor = imagecolorallocatealpha( $destination, 255, 255, 255, 127 ); + imagealphablending( $destination, true ); + imagesavealpha( $destination, true ); + imagefill( $destination, 1, 1, $bgColor ); + + // Apply background if one is defined + if ( $this->options->background !== false ) + { + $background = $this->imageCreateFrom( $this->options->background ); + + call_user_func_array( + $this->options->resampleFunction, + array( + $destination, + $background['image'], + 0, 0, + 0, 0, + $this->options->width, $this->options->height, + $background['width'], $background['height'], + ) + ); + } + + // Draw all images to exclude them from supersampling + $destination = $this->addImages( $destination ); + + // Finally merge with graph + $image = $this->getImage(); + call_user_func_array( + $this->options->resampleFunction, + array( + $destination, + $image, + 0, 0, + 0, 0, + $this->options->width, $this->options->height, + $this->supersample( $this->options->width ), $this->supersample( $this->options->height ) + ) + ); + + $this->image = $destination; + imagedestroy( $image ); + + // Draw all texts + // Reset supersampling during text rendering + $supersampling = $this->options->supersampling; + $this->options->supersampling = 1; + $this->drawAllTexts(); + $this->options->supersampling = $supersampling; + + $image = $this->getImage(); + switch ( $this->options->imageFormat ) + { + case IMG_PNG: + if ( $file === null ) + { + imagepng( $image ); + } + else + { + imagepng( $image, $file ); + } + break; + case IMG_JPEG: + imagejpeg( $image, $file, $this->options->jpegQuality ); + break; + default: + throw new ezcGraphGdDriverUnsupportedImageTypeException( $this->options->imageFormat ); + } + } + + /** + * Get resource of rendered result + * + * Return the resource of the rendered result. You should not use this + * method before you called either renderToOutput() or render(), as the + * image may not be completely rendered until then. + * + * @return resource + */ + public function getResource() + { + return $this->image; + } +} + +?> diff --git a/library/ezc/Graph/src/driver/svg.php b/library/ezc/Graph/src/driver/svg.php index 01302c9396..110e311273 100644 --- a/library/ezc/Graph/src/driver/svg.php +++ b/library/ezc/Graph/src/driver/svg.php @@ -1,1230 +1,1230 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->background->color = '#FFFFFFFF'; - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->renderer = new ezcGraphRenderer3d(); - * $graph->renderer->options->pieChartShadowSize = 10; - * $graph->renderer->options->pieChartGleam = .5; - * $graph->renderer->options->dataBorder = false; - * $graph->renderer->options->pieChartHeight = 16; - * $graph->renderer->options->legendSymbolGleam = .5; - * - * // SVG driver options - * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg'; - * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 ); - * $graph->driver->options->insertIntoGroup = 'ezcGraph'; - * - * $graph->render( 400, 200, 'tutorial_driver_svg.svg' ); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphSvgDriver extends ezcGraphDriver -{ - - /** - * DOM tree of the svg document - * - * @var DOMDocument - */ - protected $dom; - - /** - * DOMElement containing all svg style definitions - * - * @var DOMElement - */ - protected $defs; - - /** - * DOMElement containing all svg objects - * - * @var DOMElement - */ - protected $elements; - - /** - * List of strings to draw - * array ( array( - * 'text' => array( 'strings' ), - * 'options' => ezcGraphFontOptions, - * ) - * - * @var array - */ - protected $strings = array(); - - /** - * List of already created gradients - * - * @var array - */ - protected $drawnGradients = array(); - - /** - * Numeric unique element id - * - * @var int - */ - protected $elementID = 0; - - /** - * Font storage for SVG font glyphs and kernings. - * - * @var ezcGraphSvgFont - */ - protected $font = null; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'dom' ); - $this->options = new ezcGraphSvgDriverOptions( $options ); - $this->font = new ezcGraphSvgFont(); - } - - /** - * Creates the DOM object to insert SVG nodes in. - * - * If the DOM document does not exists it will be created or loaded - * according to the settings. - * - * @return void - */ - protected function createDocument() - { - if ( $this->dom === null ) - { - // Create encoding based dom document - if ( $this->options->encoding !== null ) - { - $this->dom = new DOMDocument( '1.0', $this->options->encoding ); - } - else - { - $this->dom = new DOMDocument( '1.0' ); - } - - if ( $this->options->templateDocument !== false ) - { - $this->dom->load( $this->options->templateDocument ); - - $this->defs = $this->dom->getElementsByTagName( 'defs' )->item( 0 ); - $svg = $this->dom->getElementsByTagName( 'svg' )->item( 0 ); - } - else - { - $svg = $this->dom->createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); - $this->dom->appendChild( $svg ); - - $svg->setAttribute( 'width', $this->options->width ); - $svg->setAttribute( 'height', $this->options->height ); - $svg->setAttribute( 'version', '1.0' ); - $svg->setAttribute( 'id', $this->options->idPrefix ); - - $this->defs = $this->dom->createElement( 'defs' ); - $this->defs = $svg->appendChild( $this->defs ); - } - - if ( $this->options->insertIntoGroup !== false ) - { - // getElementById only works for Documents validated against a certain - // schema, so that the use of XPath should be faster in most cases. - $xpath = new DomXPath( $this->dom ); - $this->elements = $xpath->query( '//*[@id = \'' . $this->options->insertIntoGroup . '\']' )->item( 0 ); - if ( !$this->elements ) - { - throw new ezcGraphSvgDriverInvalidIdException( $this->options->insertIntoGroup ); - } - } - else - { - $this->elements = $this->dom->createElement( 'g' ); - $this->elements->setAttribute( 'id', $this->options->idPrefix . 'Chart' ); - $this->elements->setAttribute( 'color-rendering', $this->options->colorRendering ); - $this->elements->setAttribute( 'shape-rendering', $this->options->shapeRendering ); - $this->elements->setAttribute( 'text-rendering', $this->options->textRendering ); - $this->elements = $svg->appendChild( $this->elements ); - } - } - } - - /** - * Return gradient URL - * - * Creates the definitions needed for a gradient, if a proper gradient does - * not yet exists. In each case a URL referencing the correct gradient will - * be returned. - * - * @param ezcGraphColor $color Gradient - * @return string Gradient URL - */ - protected function getGradientUrl( ezcGraphColor $color ) - { - switch ( true ) - { - case ( $color instanceof ezcGraphLinearGradient ): - if ( !in_array( $color->__toString(), $this->drawnGradients, true ) ) - { - $gradient = $this->dom->createElement( 'linearGradient' ); - $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() ); - $this->defs->appendChild( $gradient ); - - // Start of linear gradient - $stop = $this->dom->createElement( 'stop' ); - $stop->setAttribute( 'offset', 0 ); - $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', - $color->startColor->red, - $color->startColor->green, - $color->startColor->blue, - 1 - ( $color->startColor->alpha / 255 ) - ) - ); - $gradient->appendChild( $stop ); - - // End of linear gradient - $stop = $this->dom->createElement( 'stop' ); - $stop->setAttribute( 'offset', 1 ); - $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', - $color->endColor->red, - $color->endColor->green, - $color->endColor->blue, - 1 - ( $color->endColor->alpha / 255 ) - ) - ); - $gradient->appendChild( $stop ); - - $gradient = $this->dom->createElement( 'linearGradient' ); - $gradient->setAttribute( 'id', $color->__toString() ); - $gradient->setAttribute( 'x1', sprintf( '%.4F', $color->startPoint->x ) ); - $gradient->setAttribute( 'y1', sprintf( '%.4F', $color->startPoint->y ) ); - $gradient->setAttribute( 'x2', sprintf( '%.4F', $color->endPoint->x ) ); - $gradient->setAttribute( 'y2', sprintf( '%.4F', $color->endPoint->y ) ); - $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' ); - $gradient->setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - '#Definition_' . $color->__toString() - ); - $this->defs->appendChild( $gradient ); - - $this->drawnGradients[] = $color->__toString(); - } - - return sprintf( 'url(#%s)', - $color->__toString() - ); - case ( $color instanceof ezcGraphRadialGradient ): - if ( !in_array( $color->__toString(), $this->drawnGradients, true ) ) - { - $gradient = $this->dom->createElement( 'linearGradient' ); - $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() ); - $this->defs->appendChild( $gradient ); - - // Start of linear gradient - $stop = $this->dom->createElement( 'stop' ); - $stop->setAttribute( 'offset', 0 ); - $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', - $color->startColor->red, - $color->startColor->green, - $color->startColor->blue, - 1 - ( $color->startColor->alpha / 255 ) - ) - ); - $gradient->appendChild( $stop ); - - // End of linear gradient - $stop = $this->dom->createElement( 'stop' ); - $stop->setAttribute( 'offset', 1 ); - $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', - $color->endColor->red, - $color->endColor->green, - $color->endColor->blue, - 1 - ( $color->endColor->alpha / 255 ) - ) - ); - $gradient->appendChild( $stop ); - - $gradient = $this->dom->createElement( 'radialGradient' ); - $gradient->setAttribute( 'id', $color->__toString() ); - $gradient->setAttribute( 'cx', sprintf( '%.4F', $color->center->x ) ); - $gradient->setAttribute( 'cy', sprintf( '%.4F', $color->center->y ) ); - $gradient->setAttribute( 'fx', sprintf( '%.4F', $color->center->x ) ); - $gradient->setAttribute( 'fy', sprintf( '%.4F', $color->center->y ) ); - $gradient->setAttribute( 'r', max( $color->height, $color->width ) ); - $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' ); - $gradient->setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - '#Definition_' . $color->__toString() - ); - $this->defs->appendChild( $gradient ); - - $this->drawnGradients[] = $color->__toString(); - } - - return sprintf( 'url(#%s)', - $color->__toString() - ); - default: - return false; - } - - } - - /** - * Get SVG style definition - * - * Returns a string with SVG style definitions created from color, - * fillstatus and line thickness. - * - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @param float $thickness Line thickness. - * @return string Formatstring - */ - protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - if ( $filled ) - { - if ( $url = $this->getGradientUrl( $color ) ) - { - return sprintf( 'fill: %s; stroke: none;', $url ); - } - else - { - return sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', - $color->red, - $color->green, - $color->blue, - 1 - ( $color->alpha / 255 ) - ); - } - } - else - { - if ( $url = $this->getGradientUrl( $color ) ) - { - return sprintf( 'fill: none; stroke: %s;', $url ); - } - else - { - return sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2F; stroke-linecap: %s; stroke-linejoin: %s;', - $color->red, - $color->green, - $color->blue, - $thickness, - 1 - ( $color->alpha / 255 ), - $this->options->strokeLineCap, - $this->options->strokeLineJoin - ); - } - } - } - - /** - * Draws a single polygon. - * - * @param array $points Point array - * @param ezcGraphColor $color Polygon color - * @param mixed $filled Filled - * @param float $thickness Line thickness - * @return void - */ - public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - $this->createDocument(); - - if ( !$filled ) - { - // The middle of the border is on the outline of a polygon in SVG, - // fix that: - try - { - $points = $this->reducePolygonSize( $points, $thickness / 2 ); - } - catch ( ezcGraphReducementFailedException $e ) - { - return false; - } - } - - $lastPoint = end( $points ); - $pointString = sprintf( ' M %.4F,%.4F', - $lastPoint->x + $this->options->graphOffset->x, - $lastPoint->y + $this->options->graphOffset->y - ); - - foreach ( $points as $point ) - { - $pointString .= sprintf( ' L %.4F,%.4F', - $point->x + $this->options->graphOffset->x, - $point->y + $this->options->graphOffset->y - ); - } - $pointString .= ' z '; - - $path = $this->dom->createElement( 'path' ); - $path->setAttribute( 'd', $pointString ); - - $path->setAttribute( - 'style', - $this->getStyle( $color, $filled, $thickness ) - ); - $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Polygon_' . ++$this->elementID ) ); - $this->elements->appendChild( $path ); - - return $id; - } - - /** - * Draws a line - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Line color - * @param float $thickness Line thickness - * @return void - */ - public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) - { - $this->createDocument(); - - $pointString = sprintf( ' M %.4F,%.4F L %.4F,%.4F', - $start->x + $this->options->graphOffset->x, - $start->y + $this->options->graphOffset->y, - $end->x + $this->options->graphOffset->x, - $end->y + $this->options->graphOffset->y - ); - - $path = $this->dom->createElement( 'path' ); - $path->setAttribute( 'd', $pointString ); - $path->setAttribute( - 'style', - $this->getStyle( $color, false, $thickness ) - ); - - $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Line_' . ++$this->elementID ) ); - $this->elements->appendChild( $path ); - - return $id; - } - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) - { - if ( $font->type === ezcGraph::SVG_FONT ) - { - return new ezcGraphBoundings( - 0, - 0, - $this->font->calculateStringWidth( $font->path, $text ) * $size, - $size - ); - } - else - { - // If we didn't get a SVG font, continue guessing the font width. - return new ezcGraphBoundings( - 0, - 0, - $this->getTextWidth( $text, $size ), - $size - ); - } - } - - /** - * Writes text in a box of desired size - * - * @param string $string Text - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of text box - * @param float $height Height of text box - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) - { - $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); - - $width -= $padding * 2; - $height -= $padding * 2; - $textPosition = new ezcGraphCoordinate( - $position->x + $padding, - $position->y + $padding - ); - - // Try to get a font size for the text to fit into the box - $maxSize = min( $height, $this->options->font->maxFontSize ); - $result = false; - for ( $size = $maxSize; $size >= $this->options->font->minFontSize; ) - { - $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); - if ( is_array( $result ) ) - { - break; - } - $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) ); - } - - if ( !is_array( $result ) ) - { - if ( ( $height >= $this->options->font->minFontSize ) && - ( $this->options->autoShortenString ) ) - { - $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); - } - else - { - throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); - } - } - - $this->options->font->minimalUsedFont = $size; - $this->strings[] = array( - 'text' => $result, - 'id' => $id = ( $this->options->idPrefix . 'TextBox_' . ++$this->elementID ), - 'position' => $textPosition, - 'width' => $width, - 'height' => $height, - 'align' => $align, - 'font' => $this->options->font, - 'rotation' => $rotation, - ); - - return $id; - } - - /** - * Guess text width for string - * - * The is no way to know the font or fontsize used by the SVG renderer to - * render the string. We assume some character width defined in the SVG - * driver options, tu guess the length of a string. We discern between - * numeric an non numeric strings, because we often use only numeric - * strings to display chart data and numbers tend to be a bit wider then - * characters. - * - * @param mixed $string - * @param mixed $size - * @access protected - * @return void - */ - protected function getTextWidth( $string, $size ) - { - switch ( strtolower( $this->options->encoding ) ) - { - case '': - case 'utf-8': - case 'utf-16': - $string = utf8_decode( $string ); - break; - } - - if ( is_numeric( $string ) ) - { - return $size * strlen( $string ) * $this->options->assumedNumericCharacterWidth; - } - else - { - return $size * strlen( $string ) * $this->options->assumedTextCharacterWidth; - } - } - - /** - * Encodes non-utf-8 strings - * - * Transforms non-utf-8 strings to their hex entities, because ext/DOM - * fails here with conversion errors. - * - * @param string $string - * @return string - */ - protected function encode( $string ) - { - $string = htmlspecialchars( $string ); - - switch ( strtolower( $this->options->encoding ) ) - { - case '': - case 'utf-8': - case 'utf-16': - return $string; - default: - // Manual escaping of non ANSII characters, because ext/DOM fails here - return preg_replace_callback( - '/[\\x80-\\xFF]/', - create_function( - '$char', - 'return sprintf( \'&#x%02x;\', ord( $char[0] ) );' - ), - $string - ); - } - } - - /** - * Draw all collected texts - * - * The texts are collected and their maximum possible font size is - * calculated. This function finally draws the texts on the image, this - * delayed drawing has two reasons: - * - * 1) This way the text strings are always on top of the image, what - * results in better readable texts - * 2) The maximum possible font size can be calculated for a set of texts - * with the same font configuration. Strings belonging to one chart - * element normally have the same font configuration, so that all texts - * belonging to one element will have the same font size. - * - * @access protected - * @return void - */ - protected function drawAllTexts() - { - $elementsRoot = $this->elements; - - foreach ( $this->strings as $text ) - { - // Add all text elements into one group - $group = $this->dom->createElement( 'g' ); - $group->setAttribute( 'id', $text['id'] ); - - if ( $text['rotation'] !== null ) - { - $group->setAttribute( 'transform', sprintf( 'rotate( %.2F %.4F %.4F )', - $text['rotation']->getRotation(), - $text['rotation']->getCenter()->x, - $text['rotation']->getCenter()->y - ) ); - } - - $group = $elementsRoot->appendChild( $group ); - - $size = $text['font']->minimalUsedFont; - $font = $text['font']->name; - - $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; - - // Calculate y offset for vertical alignement - switch ( true ) - { - case ( $text['align'] & ezcGraph::BOTTOM ): - $yOffset = $text['height'] - $completeHeight; - break; - case ( $text['align'] & ezcGraph::MIDDLE ): - $yOffset = ( $text['height'] - $completeHeight ) / 2; - break; - case ( $text['align'] & ezcGraph::TOP ): - default: - $yOffset = 0; - break; - } - - $padding = $text['font']->padding + $text['font']->borderWidth / 2; - if ( $this->options->font->minimizeBorder === true ) - { - // Calculate maximum width of text rows - $width = false; - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - if ( ( $strWidth = $this->getTextBoundings( $size, $text['font'], $string )->width ) > $width ) - { - $width = $strWidth; - } - } - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $xOffset = 0; - break; - case ( $text['align'] & ezcGraph::CENTER ): - $xOffset = ( $text['width'] - $width ) / 2; - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $xOffset = $text['width'] - $width; - break; - } - - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y - $padding + $yOffset - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $xOffset + $width, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - new ezcGraphCoordinate( - $text['position']->x - $padding + $xOffset, - $text['position']->y + $padding * 2 + $yOffset + $completeHeight - ), - ); - } - else - { - $borderPolygonArray = array( - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y - $padding - ), - new ezcGraphCoordinate( - $text['position']->x + $padding * 2 + $text['width'], - $text['position']->y + $padding * 2 + $text['height'] - ), - new ezcGraphCoordinate( - $text['position']->x - $padding, - $text['position']->y + $padding * 2 + $text['height'] - ), - ); - } - - // Set elements root temporary to local text group to ensure - // background and border beeing elements of text group - $this->elements = $group; - if ( $text['font']->background !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->background, - true - ); - } - else - { - // Always draw full tranparent background polygon as fallback, - // to be able to click on complete font space, not only on - // the text - $this->drawPolygon( - $borderPolygonArray, - ezcGraphColor::fromHex( '#FFFFFFFF' ), - true - ); - } - - if ( $text['font']->border !== false ) - { - $this->drawPolygon( - $borderPolygonArray, - $text['font']->border, - false, - $text['font']->borderWidth - ); - } - $this->elements = $elementsRoot; - - // Bottom line for SVG fonts is lifted a bit - $text['position']->y += $size * .85; - - // Render text with evaluated font size - foreach ( $text['text'] as $line ) - { - $string = implode( ' ', $line ); - - switch ( true ) - { - case ( $text['align'] & ezcGraph::LEFT ): - $position = new ezcGraphCoordinate( - $text['position']->x, - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::RIGHT ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ), - $text['position']->y + $yOffset - ); - break; - case ( $text['align'] & ezcGraph::CENTER ): - $position = new ezcGraphCoordinate( - $text['position']->x + ( ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ) / 2 ), - $text['position']->y + $yOffset - ); - break; - } - - // Optionally draw text shadow - if ( $text['font']->textShadow === true ) - { - $textNode = $this->dom->createElement( 'text', $this->encode( $string ) ); - $textNode->setAttribute( 'id', $text['id'] . '_shadow' ); - $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x + $text['font']->textShadowOffset ) ); - $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) ); - $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y + $text['font']->textShadowOffset ) ); - $textNode->setAttribute( - 'style', - sprintf( - 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', - $size, - $text['font']->name, - $text['font']->textShadowColor->red, - $text['font']->textShadowColor->green, - $text['font']->textShadowColor->blue, - 1 - ( $text['font']->textShadowColor->alpha / 255 ) - ) - ); - $group->appendChild( $textNode ); - } - - // Finally draw text - $textNode = $this->dom->createElement( 'text', $this->encode( $string ) ); - $textNode->setAttribute( 'id', $text['id'] . '_text' ); - $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) ); - $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) ); - $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) ); - $textNode->setAttribute( - 'style', - sprintf( - 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', - $size, - $text['font']->name, - $text['font']->color->red, - $text['font']->color->green, - $text['font']->color->blue, - 1 - ( $text['font']->color->alpha / 255 ) - ) - ); - $group->appendChild( $textNode ); - - $text['position']->y += $size + $size * $this->options->lineSpacing; - } - } - } - - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center Center of circle - * @param mixed $width Width - * @param mixed $height Height - * @param mixed $startAngle Start angle of circle sector - * @param mixed $endAngle End angle of circle sector - * @param ezcGraphColor $color Color - * @param mixed $filled Filled; - * @return void - */ - public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $this->createDocument(); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - if ( ( $endAngle - $startAngle ) >= 360 ) - { - return $this->drawCircle( $center, $width, $height, $color, $filled ); - } - - // We need the radius - $width /= 2; - $height /= 2; - - // Apply offset to copy of center coordinate - $center = clone $center; - $center->x += $this->options->graphOffset->x; - $center->y += $this->options->graphOffset->y; - - if ( $filled ) - { - $Xstart = $center->x + $width * cos( -deg2rad( $startAngle ) ); - $Ystart = $center->y + $height * sin( deg2rad( $startAngle ) ); - $Xend = $center->x + $width * cos( ( -deg2rad( $endAngle ) ) ); - $Yend = $center->y + $height * sin( ( deg2rad( $endAngle ) ) ); - - $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z', - // Middle - $center->x, $center->y, - // Startpoint - $Xstart, $Ystart, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Endpoint - $Xend, $Yend - ) - ); - - $arc->setAttribute( - 'style', - $this->getStyle( $color, $filled, 1 ) - ); - $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) ); - $this->elements->appendChild( $arc ); - return $id; - } - else - { - try - { - $reduced = $this->reduceEllipseSize( $center, $width * 2, $height * 2, $startAngle, $endAngle, .5 ); - } - catch ( ezcGraphReducementFailedException $e ) - { - return false; - } - - $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z', - // Middle - $reduced['center']->x, $reduced['center']->y, - // Startpoint - $reduced['start']->x, $reduced['start']->y, - // Radius - $width - .5, $height - .5, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Endpoint - $reduced['end']->x, $reduced['end']->y - ) - ); - - $arc->setAttribute( - 'style', - $this->getStyle( $color, $filled, 1 ) - ); - - $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) ); - $this->elements->appendChild( $arc ); - - return $id; - } - } - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled - * @return void - */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - $this->createDocument(); - - // Normalize angles - if ( $startAngle > $endAngle ) - { - $tmp = $startAngle; - $startAngle = $endAngle; - $endAngle = $tmp; - } - - if ( ( $endAngle - $startAngle > 180 ) || - ( ( $startAngle % 180 != 0) && ( $endAngle % 180 != 0) && ( ( $startAngle % 360 > 180 ) XOR ( $endAngle % 360 > 180 ) ) ) ) - { - // Border crosses he 180 degrees border - $intersection = floor( $endAngle / 180 ) * 180; - while ( $intersection >= $endAngle ) - { - $intersection -= 180; - } - - $this->drawCircularArc( $center, $width, $height, $size, $startAngle, $intersection, $color, $filled ); - $this->drawCircularArc( $center, $width, $height, $size, $intersection, $endAngle, $color, $filled ); - return; - } - - // We need the radius - $width /= 2; - $height /= 2; - - $Xstart = $center->x + $this->options->graphOffset->x + $width * cos( -deg2rad( $startAngle ) ); - $Ystart = $center->y + $this->options->graphOffset->y + $height * sin( deg2rad( $startAngle ) ); - $Xend = $center->x + $this->options->graphOffset->x + $width * cos( ( -deg2rad( $endAngle ) ) ); - $Yend = $center->y + $this->options->graphOffset->y + $height * sin( ( deg2rad( $endAngle ) ) ); - - if ( $filled === true ) - { - $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z', - // Endpoint low - $Xend, $Yend + $size, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Startpoint low - $Xstart, $Ystart + $size, - // Startpoint - $Xstart, $Ystart, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Endpoint - $Xend, $Yend - ) - ); - } - else - { - $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F', - // Startpoint - $Xstart, $Ystart, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Endpoint - $Xend, $Yend - ) - ); - } - - $arc->setAttribute( - 'style', - $this->getStyle( $color, $filled ) - ); - - $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) ); - $this->elements->appendChild( $arc ); - - if ( ( $this->options->shadeCircularArc !== false ) && - $filled ) - { - $gradient = new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $center->x - $width, - $center->y - ), - new ezcGraphCoordinate( - $center->x + $width, - $center->y - ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), - ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc ) - ); - - $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z', - // Endpoint low - $Xend, $Yend + $size, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Startpoint low - $Xstart, $Ystart + $size, - // Startpoint - $Xstart, $Ystart, - // Radius - $width, $height, - // SVG-Stuff - ( $endAngle - $startAngle ) > 180, - // Endpoint - $Xend, $Yend - ) - ); - - $arc->setAttribute( - 'style', - $this->getStyle( $gradient, $filled ) - ); - $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) ); - - $this->elements->appendChild( $arc ); - } - - return $id; - } - - /** - * Draw circle - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param mixed $width Width of ellipse - * @param mixed $height height of ellipse - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) - { - $this->createDocument(); - - $ellipse = $this->dom->createElement( 'ellipse' ); - $ellipse->setAttribute( 'cx', sprintf( '%.4F', $center->x + $this->options->graphOffset->x ) ); - $ellipse->setAttribute( 'cy', sprintf( '%.4F', $center->y + $this->options->graphOffset->y ) ); - $ellipse->setAttribute( 'rx', sprintf( '%.4F', $width / 2 - ( $filled ? 0 : .5 ) ) ); - $ellipse->setAttribute( 'ry', sprintf( '%.4F', $height / 2 - ( $filled ? 0 : .5 ) ) ); - - $ellipse->setAttribute( - 'style', - $this->getStyle( $color, $filled, 1 ) - ); - - $ellipse->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Circle_' . ++$this->elementID ) ); - $this->elements->appendChild( $ellipse ); - - return $id; - } - - /** - * Draw an image - * - * The image will be inlined in the SVG document using data URL scheme. For - * this the mime type and base64 encoded file content will be merged to - * URL. - * - * @param mixed $file Image file - * @param ezcGraphCoordinate $position Top left position - * @param mixed $width Width of image in destination image - * @param mixed $height Height of image in destination image - * @return void - */ - public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) - { - $this->createDocument(); - - $data = getimagesize( $file ); - $image = $this->dom->createElement( 'image' ); - - $image->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) ); - $image->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) ); - $image->setAttribute( 'width', sprintf( '%.4Fpx', $width ) ); - $image->setAttribute( 'height', sprintf( '%.4Fpx', $height ) ); - $image->setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - sprintf( 'data:%s;base64,%s', - $data['mime'], - base64_encode( file_get_contents( $file ) ) - ) - ); - - $this->elements->appendChild( $image ); - $image->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Image_' . ++$this->elementID ) ); - - return $id; - } - - /** - * Return mime type for current image format - * - * @return string - */ - public function getMimeType() - { - return 'image/svg+xml'; - } - - /** - * Render image directly to output - * - * The method renders the image directly to the standard output. You - * normally do not want to use this function, because it makes it harder - * to proper cache the generated graphs. - * - * @return void - */ - public function renderToOutput() - { - $this->createDocument(); - $this->drawAllTexts(); - - header( 'Content-Type: ' . $this->getMimeType() ); - echo $this->dom->saveXML(); - } - - /** - * Finally save image - * - * @param string $file Destination filename - * @return void - */ - public function render( $file ) - { - $this->createDocument(); - $this->drawAllTexts(); - - // Embed used glyphs - $this->font->addFontToDocument( $this->dom ); - $this->dom->save( $file ); - } - - /** - * Get resource of rendered result - * - * Return the resource of the rendered result. You should not use this - * method before you called either renderToOutput() or render(), as the - * image may not be completely rendered until then. - * - * @return DOMDocument - */ - public function getResource() - { - return $this->dom; - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->background->color = '#FFFFFFFF'; + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->renderer = new ezcGraphRenderer3d(); + * $graph->renderer->options->pieChartShadowSize = 10; + * $graph->renderer->options->pieChartGleam = .5; + * $graph->renderer->options->dataBorder = false; + * $graph->renderer->options->pieChartHeight = 16; + * $graph->renderer->options->legendSymbolGleam = .5; + * + * // SVG driver options + * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg'; + * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 ); + * $graph->driver->options->insertIntoGroup = 'ezcGraph'; + * + * $graph->render( 400, 200, 'tutorial_driver_svg.svg' ); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphSvgDriver extends ezcGraphDriver +{ + + /** + * DOM tree of the svg document + * + * @var DOMDocument + */ + protected $dom; + + /** + * DOMElement containing all svg style definitions + * + * @var DOMElement + */ + protected $defs; + + /** + * DOMElement containing all svg objects + * + * @var DOMElement + */ + protected $elements; + + /** + * List of strings to draw + * array ( array( + * 'text' => array( 'strings' ), + * 'options' => ezcGraphFontOptions, + * ) + * + * @var array + */ + protected $strings = array(); + + /** + * List of already created gradients + * + * @var array + */ + protected $drawnGradients = array(); + + /** + * Numeric unique element id + * + * @var int + */ + protected $elementID = 0; + + /** + * Font storage for SVG font glyphs and kernings. + * + * @var ezcGraphSvgFont + */ + protected $font = null; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'dom' ); + $this->options = new ezcGraphSvgDriverOptions( $options ); + $this->font = new ezcGraphSvgFont(); + } + + /** + * Creates the DOM object to insert SVG nodes in. + * + * If the DOM document does not exists it will be created or loaded + * according to the settings. + * + * @return void + */ + protected function createDocument() + { + if ( $this->dom === null ) + { + // Create encoding based dom document + if ( $this->options->encoding !== null ) + { + $this->dom = new DOMDocument( '1.0', $this->options->encoding ); + } + else + { + $this->dom = new DOMDocument( '1.0' ); + } + + if ( $this->options->templateDocument !== false ) + { + $this->dom->load( $this->options->templateDocument ); + + $this->defs = $this->dom->getElementsByTagName( 'defs' )->item( 0 ); + $svg = $this->dom->getElementsByTagName( 'svg' )->item( 0 ); + } + else + { + $svg = $this->dom->createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); + $this->dom->appendChild( $svg ); + + $svg->setAttribute( 'width', $this->options->width ); + $svg->setAttribute( 'height', $this->options->height ); + $svg->setAttribute( 'version', '1.0' ); + $svg->setAttribute( 'id', $this->options->idPrefix ); + + $this->defs = $this->dom->createElement( 'defs' ); + $this->defs = $svg->appendChild( $this->defs ); + } + + if ( $this->options->insertIntoGroup !== false ) + { + // getElementById only works for Documents validated against a certain + // schema, so that the use of XPath should be faster in most cases. + $xpath = new DomXPath( $this->dom ); + $this->elements = $xpath->query( '//*[@id = \'' . $this->options->insertIntoGroup . '\']' )->item( 0 ); + if ( !$this->elements ) + { + throw new ezcGraphSvgDriverInvalidIdException( $this->options->insertIntoGroup ); + } + } + else + { + $this->elements = $this->dom->createElement( 'g' ); + $this->elements->setAttribute( 'id', $this->options->idPrefix . 'Chart' ); + $this->elements->setAttribute( 'color-rendering', $this->options->colorRendering ); + $this->elements->setAttribute( 'shape-rendering', $this->options->shapeRendering ); + $this->elements->setAttribute( 'text-rendering', $this->options->textRendering ); + $this->elements = $svg->appendChild( $this->elements ); + } + } + } + + /** + * Return gradient URL + * + * Creates the definitions needed for a gradient, if a proper gradient does + * not yet exists. In each case a URL referencing the correct gradient will + * be returned. + * + * @param ezcGraphColor $color Gradient + * @return string Gradient URL + */ + protected function getGradientUrl( ezcGraphColor $color ) + { + switch ( true ) + { + case ( $color instanceof ezcGraphLinearGradient ): + if ( !in_array( $color->__toString(), $this->drawnGradients, true ) ) + { + $gradient = $this->dom->createElement( 'linearGradient' ); + $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() ); + $this->defs->appendChild( $gradient ); + + // Start of linear gradient + $stop = $this->dom->createElement( 'stop' ); + $stop->setAttribute( 'offset', 0 ); + $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', + $color->startColor->red, + $color->startColor->green, + $color->startColor->blue, + 1 - ( $color->startColor->alpha / 255 ) + ) + ); + $gradient->appendChild( $stop ); + + // End of linear gradient + $stop = $this->dom->createElement( 'stop' ); + $stop->setAttribute( 'offset', 1 ); + $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', + $color->endColor->red, + $color->endColor->green, + $color->endColor->blue, + 1 - ( $color->endColor->alpha / 255 ) + ) + ); + $gradient->appendChild( $stop ); + + $gradient = $this->dom->createElement( 'linearGradient' ); + $gradient->setAttribute( 'id', $color->__toString() ); + $gradient->setAttribute( 'x1', sprintf( '%.4F', $color->startPoint->x ) ); + $gradient->setAttribute( 'y1', sprintf( '%.4F', $color->startPoint->y ) ); + $gradient->setAttribute( 'x2', sprintf( '%.4F', $color->endPoint->x ) ); + $gradient->setAttribute( 'y2', sprintf( '%.4F', $color->endPoint->y ) ); + $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' ); + $gradient->setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + '#Definition_' . $color->__toString() + ); + $this->defs->appendChild( $gradient ); + + $this->drawnGradients[] = $color->__toString(); + } + + return sprintf( 'url(#%s)', + $color->__toString() + ); + case ( $color instanceof ezcGraphRadialGradient ): + if ( !in_array( $color->__toString(), $this->drawnGradients, true ) ) + { + $gradient = $this->dom->createElement( 'linearGradient' ); + $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() ); + $this->defs->appendChild( $gradient ); + + // Start of linear gradient + $stop = $this->dom->createElement( 'stop' ); + $stop->setAttribute( 'offset', 0 ); + $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', + $color->startColor->red, + $color->startColor->green, + $color->startColor->blue, + 1 - ( $color->startColor->alpha / 255 ) + ) + ); + $gradient->appendChild( $stop ); + + // End of linear gradient + $stop = $this->dom->createElement( 'stop' ); + $stop->setAttribute( 'offset', 1 ); + $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;', + $color->endColor->red, + $color->endColor->green, + $color->endColor->blue, + 1 - ( $color->endColor->alpha / 255 ) + ) + ); + $gradient->appendChild( $stop ); + + $gradient = $this->dom->createElement( 'radialGradient' ); + $gradient->setAttribute( 'id', $color->__toString() ); + $gradient->setAttribute( 'cx', sprintf( '%.4F', $color->center->x ) ); + $gradient->setAttribute( 'cy', sprintf( '%.4F', $color->center->y ) ); + $gradient->setAttribute( 'fx', sprintf( '%.4F', $color->center->x ) ); + $gradient->setAttribute( 'fy', sprintf( '%.4F', $color->center->y ) ); + $gradient->setAttribute( 'r', max( $color->height, $color->width ) ); + $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' ); + $gradient->setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + '#Definition_' . $color->__toString() + ); + $this->defs->appendChild( $gradient ); + + $this->drawnGradients[] = $color->__toString(); + } + + return sprintf( 'url(#%s)', + $color->__toString() + ); + default: + return false; + } + + } + + /** + * Get SVG style definition + * + * Returns a string with SVG style definitions created from color, + * fillstatus and line thickness. + * + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @param float $thickness Line thickness. + * @return string Formatstring + */ + protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + if ( $filled ) + { + if ( $url = $this->getGradientUrl( $color ) ) + { + return sprintf( 'fill: %s; stroke: none;', $url ); + } + else + { + return sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', + $color->red, + $color->green, + $color->blue, + 1 - ( $color->alpha / 255 ) + ); + } + } + else + { + if ( $url = $this->getGradientUrl( $color ) ) + { + return sprintf( 'fill: none; stroke: %s;', $url ); + } + else + { + return sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2F; stroke-linecap: %s; stroke-linejoin: %s;', + $color->red, + $color->green, + $color->blue, + $thickness, + 1 - ( $color->alpha / 255 ), + $this->options->strokeLineCap, + $this->options->strokeLineJoin + ); + } + } + } + + /** + * Draws a single polygon. + * + * @param array $points Point array + * @param ezcGraphColor $color Polygon color + * @param mixed $filled Filled + * @param float $thickness Line thickness + * @return void + */ + public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + $this->createDocument(); + + if ( !$filled ) + { + // The middle of the border is on the outline of a polygon in SVG, + // fix that: + try + { + $points = $this->reducePolygonSize( $points, $thickness / 2 ); + } + catch ( ezcGraphReducementFailedException $e ) + { + return false; + } + } + + $lastPoint = end( $points ); + $pointString = sprintf( ' M %.4F,%.4F', + $lastPoint->x + $this->options->graphOffset->x, + $lastPoint->y + $this->options->graphOffset->y + ); + + foreach ( $points as $point ) + { + $pointString .= sprintf( ' L %.4F,%.4F', + $point->x + $this->options->graphOffset->x, + $point->y + $this->options->graphOffset->y + ); + } + $pointString .= ' z '; + + $path = $this->dom->createElement( 'path' ); + $path->setAttribute( 'd', $pointString ); + + $path->setAttribute( + 'style', + $this->getStyle( $color, $filled, $thickness ) + ); + $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Polygon_' . ++$this->elementID ) ); + $this->elements->appendChild( $path ); + + return $id; + } + + /** + * Draws a line + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Line color + * @param float $thickness Line thickness + * @return void + */ + public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) + { + $this->createDocument(); + + $pointString = sprintf( ' M %.4F,%.4F L %.4F,%.4F', + $start->x + $this->options->graphOffset->x, + $start->y + $this->options->graphOffset->y, + $end->x + $this->options->graphOffset->x, + $end->y + $this->options->graphOffset->y + ); + + $path = $this->dom->createElement( 'path' ); + $path->setAttribute( 'd', $pointString ); + $path->setAttribute( + 'style', + $this->getStyle( $color, false, $thickness ) + ); + + $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Line_' . ++$this->elementID ) ); + $this->elements->appendChild( $path ); + + return $id; + } + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) + { + if ( $font->type === ezcGraph::SVG_FONT ) + { + return new ezcGraphBoundings( + 0, + 0, + $this->font->calculateStringWidth( $font->path, $text ) * $size, + $size + ); + } + else + { + // If we didn't get a SVG font, continue guessing the font width. + return new ezcGraphBoundings( + 0, + 0, + $this->getTextWidth( $text, $size ), + $size + ); + } + } + + /** + * Writes text in a box of desired size + * + * @param string $string Text + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of text box + * @param float $height Height of text box + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) + { + $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 ); + + $width -= $padding * 2; + $height -= $padding * 2; + $textPosition = new ezcGraphCoordinate( + $position->x + $padding, + $position->y + $padding + ); + + // Try to get a font size for the text to fit into the box + $maxSize = min( $height, $this->options->font->maxFontSize ); + $result = false; + for ( $size = $maxSize; $size >= $this->options->font->minFontSize; ) + { + $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size ); + if ( is_array( $result ) ) + { + break; + } + $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) ); + } + + if ( !is_array( $result ) ) + { + if ( ( $height >= $this->options->font->minFontSize ) && + ( $this->options->autoShortenString ) ) + { + $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize ); + } + else + { + throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height ); + } + } + + $this->options->font->minimalUsedFont = $size; + $this->strings[] = array( + 'text' => $result, + 'id' => $id = ( $this->options->idPrefix . 'TextBox_' . ++$this->elementID ), + 'position' => $textPosition, + 'width' => $width, + 'height' => $height, + 'align' => $align, + 'font' => $this->options->font, + 'rotation' => $rotation, + ); + + return $id; + } + + /** + * Guess text width for string + * + * The is no way to know the font or fontsize used by the SVG renderer to + * render the string. We assume some character width defined in the SVG + * driver options, tu guess the length of a string. We discern between + * numeric an non numeric strings, because we often use only numeric + * strings to display chart data and numbers tend to be a bit wider then + * characters. + * + * @param mixed $string + * @param mixed $size + * @access protected + * @return void + */ + protected function getTextWidth( $string, $size ) + { + switch ( strtolower( $this->options->encoding ) ) + { + case '': + case 'utf-8': + case 'utf-16': + $string = utf8_decode( $string ); + break; + } + + if ( is_numeric( $string ) ) + { + return $size * strlen( $string ) * $this->options->assumedNumericCharacterWidth; + } + else + { + return $size * strlen( $string ) * $this->options->assumedTextCharacterWidth; + } + } + + /** + * Encodes non-utf-8 strings + * + * Transforms non-utf-8 strings to their hex entities, because ext/DOM + * fails here with conversion errors. + * + * @param string $string + * @return string + */ + protected function encode( $string ) + { + $string = htmlspecialchars( $string ); + + switch ( strtolower( $this->options->encoding ) ) + { + case '': + case 'utf-8': + case 'utf-16': + return $string; + default: + // Manual escaping of non ANSII characters, because ext/DOM fails here + return preg_replace_callback( + '/[\\x80-\\xFF]/', + create_function( + '$char', + 'return sprintf( \'&#x%02x;\', ord( $char[0] ) );' + ), + $string + ); + } + } + + /** + * Draw all collected texts + * + * The texts are collected and their maximum possible font size is + * calculated. This function finally draws the texts on the image, this + * delayed drawing has two reasons: + * + * 1) This way the text strings are always on top of the image, what + * results in better readable texts + * 2) The maximum possible font size can be calculated for a set of texts + * with the same font configuration. Strings belonging to one chart + * element normally have the same font configuration, so that all texts + * belonging to one element will have the same font size. + * + * @access protected + * @return void + */ + protected function drawAllTexts() + { + $elementsRoot = $this->elements; + + foreach ( $this->strings as $text ) + { + // Add all text elements into one group + $group = $this->dom->createElement( 'g' ); + $group->setAttribute( 'id', $text['id'] ); + + if ( $text['rotation'] !== null ) + { + $group->setAttribute( 'transform', sprintf( 'rotate( %.2F %.4F %.4F )', + $text['rotation']->getRotation(), + $text['rotation']->getCenter()->x, + $text['rotation']->getCenter()->y + ) ); + } + + $group = $elementsRoot->appendChild( $group ); + + $size = $text['font']->minimalUsedFont; + $font = $text['font']->name; + + $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing; + + // Calculate y offset for vertical alignement + switch ( true ) + { + case ( $text['align'] & ezcGraph::BOTTOM ): + $yOffset = $text['height'] - $completeHeight; + break; + case ( $text['align'] & ezcGraph::MIDDLE ): + $yOffset = ( $text['height'] - $completeHeight ) / 2; + break; + case ( $text['align'] & ezcGraph::TOP ): + default: + $yOffset = 0; + break; + } + + $padding = $text['font']->padding + $text['font']->borderWidth / 2; + if ( $this->options->font->minimizeBorder === true ) + { + // Calculate maximum width of text rows + $width = false; + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + if ( ( $strWidth = $this->getTextBoundings( $size, $text['font'], $string )->width ) > $width ) + { + $width = $strWidth; + } + } + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $xOffset = 0; + break; + case ( $text['align'] & ezcGraph::CENTER ): + $xOffset = ( $text['width'] - $width ) / 2; + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $xOffset = $text['width'] - $width; + break; + } + + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y - $padding + $yOffset + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $xOffset + $width, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + new ezcGraphCoordinate( + $text['position']->x - $padding + $xOffset, + $text['position']->y + $padding * 2 + $yOffset + $completeHeight + ), + ); + } + else + { + $borderPolygonArray = array( + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y - $padding + ), + new ezcGraphCoordinate( + $text['position']->x + $padding * 2 + $text['width'], + $text['position']->y + $padding * 2 + $text['height'] + ), + new ezcGraphCoordinate( + $text['position']->x - $padding, + $text['position']->y + $padding * 2 + $text['height'] + ), + ); + } + + // Set elements root temporary to local text group to ensure + // background and border beeing elements of text group + $this->elements = $group; + if ( $text['font']->background !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->background, + true + ); + } + else + { + // Always draw full tranparent background polygon as fallback, + // to be able to click on complete font space, not only on + // the text + $this->drawPolygon( + $borderPolygonArray, + ezcGraphColor::fromHex( '#FFFFFFFF' ), + true + ); + } + + if ( $text['font']->border !== false ) + { + $this->drawPolygon( + $borderPolygonArray, + $text['font']->border, + false, + $text['font']->borderWidth + ); + } + $this->elements = $elementsRoot; + + // Bottom line for SVG fonts is lifted a bit + $text['position']->y += $size * .85; + + // Render text with evaluated font size + foreach ( $text['text'] as $line ) + { + $string = implode( ' ', $line ); + + switch ( true ) + { + case ( $text['align'] & ezcGraph::LEFT ): + $position = new ezcGraphCoordinate( + $text['position']->x, + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::RIGHT ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ), + $text['position']->y + $yOffset + ); + break; + case ( $text['align'] & ezcGraph::CENTER ): + $position = new ezcGraphCoordinate( + $text['position']->x + ( ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ) / 2 ), + $text['position']->y + $yOffset + ); + break; + } + + // Optionally draw text shadow + if ( $text['font']->textShadow === true ) + { + $textNode = $this->dom->createElement( 'text', $this->encode( $string ) ); + $textNode->setAttribute( 'id', $text['id'] . '_shadow' ); + $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x + $text['font']->textShadowOffset ) ); + $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) ); + $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y + $text['font']->textShadowOffset ) ); + $textNode->setAttribute( + 'style', + sprintf( + 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', + $size, + $text['font']->name, + $text['font']->textShadowColor->red, + $text['font']->textShadowColor->green, + $text['font']->textShadowColor->blue, + 1 - ( $text['font']->textShadowColor->alpha / 255 ) + ) + ); + $group->appendChild( $textNode ); + } + + // Finally draw text + $textNode = $this->dom->createElement( 'text', $this->encode( $string ) ); + $textNode->setAttribute( 'id', $text['id'] . '_text' ); + $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) ); + $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) ); + $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) ); + $textNode->setAttribute( + 'style', + sprintf( + 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;', + $size, + $text['font']->name, + $text['font']->color->red, + $text['font']->color->green, + $text['font']->color->blue, + 1 - ( $text['font']->color->alpha / 255 ) + ) + ); + $group->appendChild( $textNode ); + + $text['position']->y += $size + $size * $this->options->lineSpacing; + } + } + } + + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center Center of circle + * @param mixed $width Width + * @param mixed $height Height + * @param mixed $startAngle Start angle of circle sector + * @param mixed $endAngle End angle of circle sector + * @param ezcGraphColor $color Color + * @param mixed $filled Filled; + * @return void + */ + public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $this->createDocument(); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + if ( ( $endAngle - $startAngle ) >= 360 ) + { + return $this->drawCircle( $center, $width, $height, $color, $filled ); + } + + // We need the radius + $width /= 2; + $height /= 2; + + // Apply offset to copy of center coordinate + $center = clone $center; + $center->x += $this->options->graphOffset->x; + $center->y += $this->options->graphOffset->y; + + if ( $filled ) + { + $Xstart = $center->x + $width * cos( -deg2rad( $startAngle ) ); + $Ystart = $center->y + $height * sin( deg2rad( $startAngle ) ); + $Xend = $center->x + $width * cos( ( -deg2rad( $endAngle ) ) ); + $Yend = $center->y + $height * sin( ( deg2rad( $endAngle ) ) ); + + $arc = $this->dom->createElement( 'path' ); + $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z', + // Middle + $center->x, $center->y, + // Startpoint + $Xstart, $Ystart, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Endpoint + $Xend, $Yend + ) + ); + + $arc->setAttribute( + 'style', + $this->getStyle( $color, $filled, 1 ) + ); + $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) ); + $this->elements->appendChild( $arc ); + return $id; + } + else + { + try + { + $reduced = $this->reduceEllipseSize( $center, $width * 2, $height * 2, $startAngle, $endAngle, .5 ); + } + catch ( ezcGraphReducementFailedException $e ) + { + return false; + } + + $arc = $this->dom->createElement( 'path' ); + $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z', + // Middle + $reduced['center']->x, $reduced['center']->y, + // Startpoint + $reduced['start']->x, $reduced['start']->y, + // Radius + $width - .5, $height - .5, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Endpoint + $reduced['end']->x, $reduced['end']->y + ) + ); + + $arc->setAttribute( + 'style', + $this->getStyle( $color, $filled, 1 ) + ); + + $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) ); + $this->elements->appendChild( $arc ); + + return $id; + } + } + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled + * @return void + */ + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + $this->createDocument(); + + // Normalize angles + if ( $startAngle > $endAngle ) + { + $tmp = $startAngle; + $startAngle = $endAngle; + $endAngle = $tmp; + } + + if ( ( $endAngle - $startAngle > 180 ) || + ( ( $startAngle % 180 != 0) && ( $endAngle % 180 != 0) && ( ( $startAngle % 360 > 180 ) XOR ( $endAngle % 360 > 180 ) ) ) ) + { + // Border crosses he 180 degrees border + $intersection = floor( $endAngle / 180 ) * 180; + while ( $intersection >= $endAngle ) + { + $intersection -= 180; + } + + $this->drawCircularArc( $center, $width, $height, $size, $startAngle, $intersection, $color, $filled ); + $this->drawCircularArc( $center, $width, $height, $size, $intersection, $endAngle, $color, $filled ); + return; + } + + // We need the radius + $width /= 2; + $height /= 2; + + $Xstart = $center->x + $this->options->graphOffset->x + $width * cos( -deg2rad( $startAngle ) ); + $Ystart = $center->y + $this->options->graphOffset->y + $height * sin( deg2rad( $startAngle ) ); + $Xend = $center->x + $this->options->graphOffset->x + $width * cos( ( -deg2rad( $endAngle ) ) ); + $Yend = $center->y + $this->options->graphOffset->y + $height * sin( ( deg2rad( $endAngle ) ) ); + + if ( $filled === true ) + { + $arc = $this->dom->createElement( 'path' ); + $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z', + // Endpoint low + $Xend, $Yend + $size, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Startpoint low + $Xstart, $Ystart + $size, + // Startpoint + $Xstart, $Ystart, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Endpoint + $Xend, $Yend + ) + ); + } + else + { + $arc = $this->dom->createElement( 'path' ); + $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F', + // Startpoint + $Xstart, $Ystart, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Endpoint + $Xend, $Yend + ) + ); + } + + $arc->setAttribute( + 'style', + $this->getStyle( $color, $filled ) + ); + + $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) ); + $this->elements->appendChild( $arc ); + + if ( ( $this->options->shadeCircularArc !== false ) && + $filled ) + { + $gradient = new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $center->x - $width, + $center->y + ), + new ezcGraphCoordinate( + $center->x + $width, + $center->y + ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ), + ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc ) + ); + + $arc = $this->dom->createElement( 'path' ); + $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z', + // Endpoint low + $Xend, $Yend + $size, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Startpoint low + $Xstart, $Ystart + $size, + // Startpoint + $Xstart, $Ystart, + // Radius + $width, $height, + // SVG-Stuff + ( $endAngle - $startAngle ) > 180, + // Endpoint + $Xend, $Yend + ) + ); + + $arc->setAttribute( + 'style', + $this->getStyle( $gradient, $filled ) + ); + $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) ); + + $this->elements->appendChild( $arc ); + } + + return $id; + } + + /** + * Draw circle + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param mixed $width Width of ellipse + * @param mixed $height height of ellipse + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) + { + $this->createDocument(); + + $ellipse = $this->dom->createElement( 'ellipse' ); + $ellipse->setAttribute( 'cx', sprintf( '%.4F', $center->x + $this->options->graphOffset->x ) ); + $ellipse->setAttribute( 'cy', sprintf( '%.4F', $center->y + $this->options->graphOffset->y ) ); + $ellipse->setAttribute( 'rx', sprintf( '%.4F', $width / 2 - ( $filled ? 0 : .5 ) ) ); + $ellipse->setAttribute( 'ry', sprintf( '%.4F', $height / 2 - ( $filled ? 0 : .5 ) ) ); + + $ellipse->setAttribute( + 'style', + $this->getStyle( $color, $filled, 1 ) + ); + + $ellipse->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Circle_' . ++$this->elementID ) ); + $this->elements->appendChild( $ellipse ); + + return $id; + } + + /** + * Draw an image + * + * The image will be inlined in the SVG document using data URL scheme. For + * this the mime type and base64 encoded file content will be merged to + * URL. + * + * @param mixed $file Image file + * @param ezcGraphCoordinate $position Top left position + * @param mixed $width Width of image in destination image + * @param mixed $height Height of image in destination image + * @return void + */ + public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) + { + $this->createDocument(); + + $data = getimagesize( $file ); + $image = $this->dom->createElement( 'image' ); + + $image->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) ); + $image->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) ); + $image->setAttribute( 'width', sprintf( '%.4Fpx', $width ) ); + $image->setAttribute( 'height', sprintf( '%.4Fpx', $height ) ); + $image->setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + sprintf( 'data:%s;base64,%s', + $data['mime'], + base64_encode( file_get_contents( $file ) ) + ) + ); + + $this->elements->appendChild( $image ); + $image->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Image_' . ++$this->elementID ) ); + + return $id; + } + + /** + * Return mime type for current image format + * + * @return string + */ + public function getMimeType() + { + return 'image/svg+xml'; + } + + /** + * Render image directly to output + * + * The method renders the image directly to the standard output. You + * normally do not want to use this function, because it makes it harder + * to proper cache the generated graphs. + * + * @return void + */ + public function renderToOutput() + { + $this->createDocument(); + $this->drawAllTexts(); + + header( 'Content-Type: ' . $this->getMimeType() ); + echo $this->dom->saveXML(); + } + + /** + * Finally save image + * + * @param string $file Destination filename + * @return void + */ + public function render( $file ) + { + $this->createDocument(); + $this->drawAllTexts(); + + // Embed used glyphs + $this->font->addFontToDocument( $this->dom ); + $this->dom->save( $file ); + } + + /** + * Get resource of rendered result + * + * Return the resource of the rendered result. You should not use this + * method before you called either renderToOutput() or render(), as the + * image may not be completely rendered until then. + * + * @return DOMDocument + */ + public function getResource() + { + return $this->dom; + } +} + +?> diff --git a/library/ezc/Graph/src/driver/svg_font.php b/library/ezc/Graph/src/driver/svg_font.php index 34fc40e03d..7858ccda82 100644 --- a/library/ezc/Graph/src/driver/svg_font.php +++ b/library/ezc/Graph/src/driver/svg_font.php @@ -3,7 +3,7 @@ * File containing the ezcGraphSVGDriver class * * @package Graph - * @version //autogentag// + * @version 1.4.3 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. * @author Freddie Witherden * @license http://ez.no/licenses/new_bsd New BSD License @@ -26,7 +26,7 @@ * ); * * - * @version //autogentag// + * @version 1.4.3 * @package Graph * @mainclass */ diff --git a/library/ezc/Graph/src/driver/verbose.php b/library/ezc/Graph/src/driver/verbose.php index 7f3ea83549..3e67e73057 100644 --- a/library/ezc/Graph/src/driver/verbose.php +++ b/library/ezc/Graph/src/driver/verbose.php @@ -1,242 +1,242 @@ -options = new ezcGraphSvgDriverOptions( $options ); - echo "\n"; - } - - /** - * Draws a single polygon - * - * @param array $points - * @param ezcGraphColor $color - * @param bool $filled - * @param float $thickness - * @return void - */ - public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) - { - $pointString = ''; - foreach ( $points as $point ) - { - $pointString .= sprintf( "\t( %.2f, %.2f )\n", $point->x, $point->y ); - } - - printf( "% 4d: Draw %spolygon:\n%s", - $this->call++, - ( $filled ? 'filled ' : '' ), - $pointString - ); - } - - /** - * Draws a single line - * - * @param ezcGraphCoordinate $start - * @param ezcGraphCoordinate $end - * @param ezcGraphColor $color - * @param float $thickness - * @return void - */ - public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) - { - printf( "% 4d: Draw line from ( %.2f, %.2f ) to ( %.2f, %.2f ) with thickness %d.\n", - $this->call++, - $start->x, - $start->y, - $end->x, - $end->y, - $thickness - ); - } - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) - { - return null; - } - - /** - * Wrties text in a box of desired size - * - * @param mixed $string - * @param ezcGraphCoordinate $position - * @param mixed $width - * @param mixed $height - * @param int $align - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) - { - printf( "% 4d: Draw text '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ) and alignement %d.\n", - $this->call++, - $string, - $position->x, - $position->y, - $width, - $height, - $align - ); - } - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center - * @param mixed $width - * @param mixed $height - * @param mixed $startAngle - * @param mixed $endAngle - * @param ezcGraphColor $color - * @param bool $filled - * @return void - */ - public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - printf( "% 4d: Draw %scicle sector at ( %.2f, %.2f ) with dimensions ( %d, %d ) from %.2f to %.2f.\n", - $this->call++, - ( $filled ? 'filled ' : '' ), - $center->x, - $center->y, - $width, - $height, - $startAngle, - $endAngle - ); - } - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled - * @return void - */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) - { - printf( "% 4d: Draw circular arc at ( %.2f, %.2f ) with dimensions ( %d, %d ) and size %.2f from %.2f to %.2f.\n", - $this->call++, - $center->x, - $center->y, - $width, - $height, - $size, - $startAngle, - $endAngle - ); - } - - /** - * Draws a circle - * - * @param ezcGraphCoordinate $center - * @param mixed $width - * @param mixed $height - * @param ezcGraphColor $color - * @param bool $filled - * - * @return void - */ - public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) - { - printf( "% 4d: Draw %scircle at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n", - $this->call++, - ( $filled ? 'filled ' : '' ), - $center->x, - $center->y, - $width, - $height - ); - } - - /** - * Draws a imagemap of desired size - * - * @param mixed $file - * @param ezcGraphCoordinate $position - * @param mixed $width - * @param mixed $height - * @return void - */ - public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) - { - printf( "% 4d: Draw image '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n", - $this->call++, - $file, - $position->x, - $position->y, - $width, - $height - ); - } - - /** - * Return mime type for current image format - * - * @return string - */ - public function getMimeType() - { - return 'text/plain'; - } - - /** - * Finally save image - * - * @param mixed $file - * @return void - */ - public function render ( $file ) - { - printf( "Render image.\n" ); - } -} - -?> +options = new ezcGraphSvgDriverOptions( $options ); + echo "\n"; + } + + /** + * Draws a single polygon + * + * @param array $points + * @param ezcGraphColor $color + * @param bool $filled + * @param float $thickness + * @return void + */ + public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ) + { + $pointString = ''; + foreach ( $points as $point ) + { + $pointString .= sprintf( "\t( %.2f, %.2f )\n", $point->x, $point->y ); + } + + printf( "% 4d: Draw %spolygon:\n%s", + $this->call++, + ( $filled ? 'filled ' : '' ), + $pointString + ); + } + + /** + * Draws a single line + * + * @param ezcGraphCoordinate $start + * @param ezcGraphCoordinate $end + * @param ezcGraphColor $color + * @param float $thickness + * @return void + */ + public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ) + { + printf( "% 4d: Draw line from ( %.2f, %.2f ) to ( %.2f, %.2f ) with thickness %d.\n", + $this->call++, + $start->x, + $start->y, + $end->x, + $end->y, + $thickness + ); + } + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ) + { + return null; + } + + /** + * Wrties text in a box of desired size + * + * @param mixed $string + * @param ezcGraphCoordinate $position + * @param mixed $width + * @param mixed $height + * @param int $align + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ) + { + printf( "% 4d: Draw text '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ) and alignement %d.\n", + $this->call++, + $string, + $position->x, + $position->y, + $width, + $height, + $align + ); + } + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center + * @param mixed $width + * @param mixed $height + * @param mixed $startAngle + * @param mixed $endAngle + * @param ezcGraphColor $color + * @param bool $filled + * @return void + */ + public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + printf( "% 4d: Draw %scicle sector at ( %.2f, %.2f ) with dimensions ( %d, %d ) from %.2f to %.2f.\n", + $this->call++, + ( $filled ? 'filled ' : '' ), + $center->x, + $center->y, + $width, + $height, + $startAngle, + $endAngle + ); + } + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled + * @return void + */ + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) + { + printf( "% 4d: Draw circular arc at ( %.2f, %.2f ) with dimensions ( %d, %d ) and size %.2f from %.2f to %.2f.\n", + $this->call++, + $center->x, + $center->y, + $width, + $height, + $size, + $startAngle, + $endAngle + ); + } + + /** + * Draws a circle + * + * @param ezcGraphCoordinate $center + * @param mixed $width + * @param mixed $height + * @param ezcGraphColor $color + * @param bool $filled + * + * @return void + */ + public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ) + { + printf( "% 4d: Draw %scircle at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n", + $this->call++, + ( $filled ? 'filled ' : '' ), + $center->x, + $center->y, + $width, + $height + ); + } + + /** + * Draws a imagemap of desired size + * + * @param mixed $file + * @param ezcGraphCoordinate $position + * @param mixed $width + * @param mixed $height + * @return void + */ + public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ) + { + printf( "% 4d: Draw image '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n", + $this->call++, + $file, + $position->x, + $position->y, + $width, + $height + ); + } + + /** + * Return mime type for current image format + * + * @return string + */ + public function getMimeType() + { + return 'text/plain'; + } + + /** + * Finally save image + * + * @param mixed $file + * @return void + */ + public function render ( $file ) + { + printf( "Render image.\n" ); + } +} + +?> diff --git a/library/ezc/Graph/src/element/axis.php b/library/ezc/Graph/src/element/axis.php index 47d443e492..3792549bc3 100644 --- a/library/ezc/Graph/src/element/axis.php +++ b/library/ezc/Graph/src/element/axis.php @@ -1,535 +1,535 @@ - - * $chart->xAxis->formatString = '%s %%'; - * - * - * For more complex formatting operations for the label you may assign a custom - * formatter function to the property $labelCallback. - * - * The orientation of labels and their position relatively to the axis ticks is - * calcualted and rendered by the ezcGraphAxisLabelRenderer classes. You can - * choose between different axis label renderer, or create you own, and assign - * an instance of one to the property $axisLabelRenderer. Currently the - * available axis label renderers are: - * - * - ezcGraphAxisBoxedLabelRenderer - * - * Renders grid and labels like commonly used in bar charts, with the label - * between two grid lines. - * - * - ezcGraphAxisCenteredLabelRenderer - * - * Centers the label right next to a tick. Commonly used for labeled axis. - * - * - ezcGraphAxisExactLabelRenderer - * - * Put the label next to each tick. Commonly used for numeric axis. - * - * - ezcGraphAxisNoLabelRenderer - * - * Renders no labels. - * - * - ezcGraphAxisRadarLabelRenderer - * - * Special label renderer for radar charts. - * - * - ezcGraphAxisRotatedLabelRenderer - * - * Accepts a rotation angle for the texts put at some axis, which might be - * useful for longer textual labels on the axis. - * - * The label renderer used by default is different depending on the axis type. - * - * @property float $nullPosition - * The position of the null value. - * @property float $axisSpace - * Percent of the chart space used to display axis labels and - * arrowheads instead of data values. - * @property float $outerAxisSpace - * Percent of the chart space used to display axis arrow at the outer - * side of the axis. If set to null, the axisSpace will be used here. - * @property ezcGraphColor $majorGrid - * Color of major majorGrid. - * @property ezcGraphColor $minorGrid - * Color of minor majorGrid. - * @property mixed $majorStep - * Labeled major steps displayed on the axis. @TODO: Should be moved - * to numeric axis. - * @property mixed $minorStep - * Non labeled minor steps on the axis. @TODO: Should be moved to - * numeric axis. - * @property string $formatString - * Formatstring to use for labeling of the axis. - * @property string $label - * Axis label - * @property int $labelSize - * Size of axis label - * @property int $labelMargin - * Distance between label an axis - * @property int $minArrowHeadSize - * Minimum Size used to draw arrow heads. - * @property int $maxArrowHeadSize - * Maximum Size used to draw arrow heads. - * @property ezcGraphAxisLabelRenderer $axisLabelRenderer - * AxisLabelRenderer used to render labels and grid on this axis. - * @property callback $labelCallback - * Callback function to format chart labels. - * Function will receive two parameters and should return a - * reformatted label. - * string function( label, step ) - * @property float $chartPosition - * Position of the axis in the chart. Only useful for additional - * axis. The basic chart axis will be automatically positioned. - * @property-read bool $initialized - * Property indicating if some values were associated with axis, or a - * scaling has been set manually. - * - * @version //autogentag// - * @package Graph - */ -abstract class ezcGraphChartElementAxis extends ezcGraphChartElement -{ - /** - * Axis label renderer class - * - * @var ezcGraphAxisLabelRenderer - */ - protected $axisLabelRenderer; - - /** - * Optionally set inner boundings. May be null depending on the used chart - * implementation. - * - * @var ezcGraphBoundings - */ - protected $innerBoundings; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['nullPosition'] = false; - $this->properties['axisSpace'] = .1; - $this->properties['outerAxisSpace'] = null; - $this->properties['majorGrid'] = false; - $this->properties['minorGrid'] = false; - $this->properties['majorStep'] = null; - $this->properties['minorStep'] = null; - $this->properties['formatString'] = '%s'; - $this->properties['label'] = false; - $this->properties['labelSize'] = 14; - $this->properties['labelMargin'] = 2; - $this->properties['minArrowHeadSize'] = 4; - $this->properties['maxArrowHeadSize'] = 8; - $this->properties['labelCallback'] = null; - $this->properties['chartPosition'] = null; - $this->properties['initialized'] = false; - - parent::__construct( $options ); - - if ( !isset( $this->axisLabelRenderer ) ) - { - $this->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer(); - } - } - - /** - * Set colors and border fro this element - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - $this->border = $palette->axisColor; - $this->padding = $palette->padding; - $this->margin = $palette->margin; - $this->majorGrid = $palette->majorGridColor; - $this->minorGrid = $palette->minorGridColor; - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'nullPosition': - $this->properties['nullPosition'] = (float) $propertyValue; - break; - case 'axisSpace': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue >= 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 1' ); - } - - $this->properties['axisSpace'] = (float) $propertyValue; - break; - /* Do not yet allow to modify this value, this need further testing. - case 'outerAxisSpace': - if ( !is_null( $propertyValue ) && - ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue >= 1 ) ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'null, or 0 <= float < 1' ); - } - - $this->properties['outerAxisSpace'] = $propertyValue; - break; - */ - case 'majorGrid': - $this->properties['majorGrid'] = ezcGraphColor::create( $propertyValue ); - break; - case 'minorGrid': - $this->properties['minorGrid'] = ezcGraphColor::create( $propertyValue ); - break; - case 'majorStep': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['majorStep'] = (float) $propertyValue; - break; - case 'minorStep': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['minorStep'] = (float) $propertyValue; - break; - case 'formatString': - $this->properties['formatString'] = (string) $propertyValue; - break; - case 'label': - $this->properties['label'] = (string) $propertyValue; - break; - case 'labelSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 6 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 6' ); - } - - $this->properties['labelSize'] = (int) $propertyValue; - break; - case 'labelMargin': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['labelMargin'] = (int) $propertyValue; - break; - case 'maxArrowHeadSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['maxArrowHeadSize'] = (int) $propertyValue; - break; - case 'minArrowHeadSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['minArrowHeadSize'] = (int) $propertyValue; - break; - case 'axisLabelRenderer': - if ( $propertyValue instanceof ezcGraphAxisLabelRenderer ) - { - $this->axisLabelRenderer = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphAxisLabelRenderer' ); - } - break; - case 'labelCallback': - if ( is_callable( $propertyValue ) ) - { - $this->properties['labelCallback'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' ); - } - break; - case 'chartPosition': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['chartPosition'] = (float) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'axisLabelRenderer': - return $this->axisLabelRenderer; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Get coordinate for a dedicated value on the chart - * - * @param float $value Value to determine position for - * @return float Position on chart - */ - abstract public function getCoordinate( $value ); - - /** - * Return count of minor steps - * - * @return integer Count of minor steps - */ - abstract public function getMinorStepCount(); - - /** - * Return count of major steps - * - * @return integer Count of major steps - */ - abstract public function getMajorStepCount(); - - /** - * Get label for a dedicated step on the axis - * - * @param integer $step Number of step - * @return string label - */ - abstract public function getLabel( $step ); - - /** - * Return array of steps on this axis - * - * @return array( ezcGraphAxisStep ) - */ - public function getSteps() - { - $majorSteps = $this->getMajorStepCount(); - $minorStepsPerMajorStepCount = ( $this->getMinorStepCount() / $majorSteps ); - - $majorStepSize = 1 / $majorSteps; - $minorStepSize = ( $minorStepsPerMajorStepCount > 0 ? $majorStepSize / $minorStepsPerMajorStepCount : 0 ); - - $steps = array(); - for ( $major = 0; $major <= $majorSteps; ++$major ) - { - $majorStep = new ezcGraphAxisStep( - $majorStepSize * $major, - $majorStepSize, - $this->getLabel( $major ), - array(), - $this->isZeroStep( $major ), - ( $major === $majorSteps ) - ); - - if ( ( $minorStepsPerMajorStepCount > 0 ) && - ( $major < $majorSteps ) ) - { - // Do not add minor steps at major steps positions - for( $minor = 1; $minor < $minorStepsPerMajorStepCount; ++$minor ) - { - $majorStep->childs[] = new ezcGraphAxisStep( - ( $majorStepSize * $major ) + ( $minorStepSize * $minor ), - $minorStepSize - ); - } - } - - $steps[] = $majorStep; - } - - return $steps; - } - - /** - * Is zero step - * - * Returns true if the given step is the one on the initial axis position - * - * @param int $step Number of step - * @return bool Status If given step is initial axis position - */ - abstract public function isZeroStep( $step ); - - /** - * Add data for this axis - * - * @param array $values - * @return void - */ - abstract public function addData( array $values ); - - /** - * Calculate axis bounding values on base of the assigned values - * - * @abstract - * @access public - * @return void - */ - abstract public function calculateAxisBoundings(); - - /** - * Render the axis - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Boundings for the axis - * @return ezcGraphBoundings Remaining boundings - */ - public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings = null ) - { - $this->innerBoundings = $innerBoundings; - $startSpace = $this->axisSpace; - $endSpace = $this->outerAxisSpace === null ? $this->axisSpace : $this->outerAxisSpace; - - switch ( $this->position ) - { - case ezcGraph::TOP: - $start = new ezcGraphCoordinate( - ( $boundings->x1 - $boundings->x0 ) * $startSpace + - $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $startSpace ), - 0 - ); - $end = new ezcGraphCoordinate( - ( $boundings->x1 - $boundings->x0 ) * $endSpace + - $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $endSpace ), - $boundings->y1 - $boundings->y0 - ); - break; - case ezcGraph::BOTTOM: - $start = new ezcGraphCoordinate( - ( $boundings->x1 - $boundings->x0 ) * $startSpace + - $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $startSpace ), - $boundings->y1 - $boundings->y0 - ); - $end = new ezcGraphCoordinate( - ( $boundings->x1 - $boundings->x0 ) * $endSpace + - $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $endSpace ), - 0 - ); - break; - case ezcGraph::LEFT: - $start = new ezcGraphCoordinate( - 0, - ( $boundings->y1 - $boundings->y0 ) * $startSpace + - $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $startSpace ) - ); - $end = new ezcGraphCoordinate( - $boundings->x1 - $boundings->x0, - ( $boundings->y1 - $boundings->y0 ) * $endSpace + - $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $endSpace ) - ); - break; - case ezcGraph::RIGHT: - $start = new ezcGraphCoordinate( - $boundings->x1 - $boundings->x0, - ( $boundings->y1 - $boundings->y0 ) * $startSpace + - $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $startSpace ) - ); - $end = new ezcGraphCoordinate( - 0, - ( $boundings->y1 - $boundings->y0 ) * $endSpace + - $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $endSpace ) - ); - break; - } - - $renderer->drawAxis( - $boundings, - $start, - $end, - $this, - $this->axisLabelRenderer, - $innerBoundings - ); - - return $boundings; - } -} - -?> + + * $chart->xAxis->formatString = '%s %%'; + * + * + * For more complex formatting operations for the label you may assign a custom + * formatter function to the property $labelCallback. + * + * The orientation of labels and their position relatively to the axis ticks is + * calcualted and rendered by the ezcGraphAxisLabelRenderer classes. You can + * choose between different axis label renderer, or create you own, and assign + * an instance of one to the property $axisLabelRenderer. Currently the + * available axis label renderers are: + * + * - ezcGraphAxisBoxedLabelRenderer + * + * Renders grid and labels like commonly used in bar charts, with the label + * between two grid lines. + * + * - ezcGraphAxisCenteredLabelRenderer + * + * Centers the label right next to a tick. Commonly used for labeled axis. + * + * - ezcGraphAxisExactLabelRenderer + * + * Put the label next to each tick. Commonly used for numeric axis. + * + * - ezcGraphAxisNoLabelRenderer + * + * Renders no labels. + * + * - ezcGraphAxisRadarLabelRenderer + * + * Special label renderer for radar charts. + * + * - ezcGraphAxisRotatedLabelRenderer + * + * Accepts a rotation angle for the texts put at some axis, which might be + * useful for longer textual labels on the axis. + * + * The label renderer used by default is different depending on the axis type. + * + * @property float $nullPosition + * The position of the null value. + * @property float $axisSpace + * Percent of the chart space used to display axis labels and + * arrowheads instead of data values. + * @property float $outerAxisSpace + * Percent of the chart space used to display axis arrow at the outer + * side of the axis. If set to null, the axisSpace will be used here. + * @property ezcGraphColor $majorGrid + * Color of major majorGrid. + * @property ezcGraphColor $minorGrid + * Color of minor majorGrid. + * @property mixed $majorStep + * Labeled major steps displayed on the axis. @TODO: Should be moved + * to numeric axis. + * @property mixed $minorStep + * Non labeled minor steps on the axis. @TODO: Should be moved to + * numeric axis. + * @property string $formatString + * Formatstring to use for labeling of the axis. + * @property string $label + * Axis label + * @property int $labelSize + * Size of axis label + * @property int $labelMargin + * Distance between label an axis + * @property int $minArrowHeadSize + * Minimum Size used to draw arrow heads. + * @property int $maxArrowHeadSize + * Maximum Size used to draw arrow heads. + * @property ezcGraphAxisLabelRenderer $axisLabelRenderer + * AxisLabelRenderer used to render labels and grid on this axis. + * @property callback $labelCallback + * Callback function to format chart labels. + * Function will receive two parameters and should return a + * reformatted label. + * string function( label, step ) + * @property float $chartPosition + * Position of the axis in the chart. Only useful for additional + * axis. The basic chart axis will be automatically positioned. + * @property-read bool $initialized + * Property indicating if some values were associated with axis, or a + * scaling has been set manually. + * + * @version 1.4.3 + * @package Graph + */ +abstract class ezcGraphChartElementAxis extends ezcGraphChartElement +{ + /** + * Axis label renderer class + * + * @var ezcGraphAxisLabelRenderer + */ + protected $axisLabelRenderer; + + /** + * Optionally set inner boundings. May be null depending on the used chart + * implementation. + * + * @var ezcGraphBoundings + */ + protected $innerBoundings; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['nullPosition'] = false; + $this->properties['axisSpace'] = .1; + $this->properties['outerAxisSpace'] = null; + $this->properties['majorGrid'] = false; + $this->properties['minorGrid'] = false; + $this->properties['majorStep'] = null; + $this->properties['minorStep'] = null; + $this->properties['formatString'] = '%s'; + $this->properties['label'] = false; + $this->properties['labelSize'] = 14; + $this->properties['labelMargin'] = 2; + $this->properties['minArrowHeadSize'] = 4; + $this->properties['maxArrowHeadSize'] = 8; + $this->properties['labelCallback'] = null; + $this->properties['chartPosition'] = null; + $this->properties['initialized'] = false; + + parent::__construct( $options ); + + if ( !isset( $this->axisLabelRenderer ) ) + { + $this->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer(); + } + } + + /** + * Set colors and border fro this element + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + $this->border = $palette->axisColor; + $this->padding = $palette->padding; + $this->margin = $palette->margin; + $this->majorGrid = $palette->majorGridColor; + $this->minorGrid = $palette->minorGridColor; + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'nullPosition': + $this->properties['nullPosition'] = (float) $propertyValue; + break; + case 'axisSpace': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue >= 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 1' ); + } + + $this->properties['axisSpace'] = (float) $propertyValue; + break; + /* Do not yet allow to modify this value, this need further testing. + case 'outerAxisSpace': + if ( !is_null( $propertyValue ) && + ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue >= 1 ) ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'null, or 0 <= float < 1' ); + } + + $this->properties['outerAxisSpace'] = $propertyValue; + break; + */ + case 'majorGrid': + $this->properties['majorGrid'] = ezcGraphColor::create( $propertyValue ); + break; + case 'minorGrid': + $this->properties['minorGrid'] = ezcGraphColor::create( $propertyValue ); + break; + case 'majorStep': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['majorStep'] = (float) $propertyValue; + break; + case 'minorStep': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['minorStep'] = (float) $propertyValue; + break; + case 'formatString': + $this->properties['formatString'] = (string) $propertyValue; + break; + case 'label': + $this->properties['label'] = (string) $propertyValue; + break; + case 'labelSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 6 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 6' ); + } + + $this->properties['labelSize'] = (int) $propertyValue; + break; + case 'labelMargin': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['labelMargin'] = (int) $propertyValue; + break; + case 'maxArrowHeadSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['maxArrowHeadSize'] = (int) $propertyValue; + break; + case 'minArrowHeadSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['minArrowHeadSize'] = (int) $propertyValue; + break; + case 'axisLabelRenderer': + if ( $propertyValue instanceof ezcGraphAxisLabelRenderer ) + { + $this->axisLabelRenderer = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphAxisLabelRenderer' ); + } + break; + case 'labelCallback': + if ( is_callable( $propertyValue ) ) + { + $this->properties['labelCallback'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' ); + } + break; + case 'chartPosition': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['chartPosition'] = (float) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'axisLabelRenderer': + return $this->axisLabelRenderer; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Get coordinate for a dedicated value on the chart + * + * @param float $value Value to determine position for + * @return float Position on chart + */ + abstract public function getCoordinate( $value ); + + /** + * Return count of minor steps + * + * @return integer Count of minor steps + */ + abstract public function getMinorStepCount(); + + /** + * Return count of major steps + * + * @return integer Count of major steps + */ + abstract public function getMajorStepCount(); + + /** + * Get label for a dedicated step on the axis + * + * @param integer $step Number of step + * @return string label + */ + abstract public function getLabel( $step ); + + /** + * Return array of steps on this axis + * + * @return array( ezcGraphAxisStep ) + */ + public function getSteps() + { + $majorSteps = $this->getMajorStepCount(); + $minorStepsPerMajorStepCount = ( $this->getMinorStepCount() / $majorSteps ); + + $majorStepSize = 1 / $majorSteps; + $minorStepSize = ( $minorStepsPerMajorStepCount > 0 ? $majorStepSize / $minorStepsPerMajorStepCount : 0 ); + + $steps = array(); + for ( $major = 0; $major <= $majorSteps; ++$major ) + { + $majorStep = new ezcGraphAxisStep( + $majorStepSize * $major, + $majorStepSize, + $this->getLabel( $major ), + array(), + $this->isZeroStep( $major ), + ( $major === $majorSteps ) + ); + + if ( ( $minorStepsPerMajorStepCount > 0 ) && + ( $major < $majorSteps ) ) + { + // Do not add minor steps at major steps positions + for( $minor = 1; $minor < $minorStepsPerMajorStepCount; ++$minor ) + { + $majorStep->childs[] = new ezcGraphAxisStep( + ( $majorStepSize * $major ) + ( $minorStepSize * $minor ), + $minorStepSize + ); + } + } + + $steps[] = $majorStep; + } + + return $steps; + } + + /** + * Is zero step + * + * Returns true if the given step is the one on the initial axis position + * + * @param int $step Number of step + * @return bool Status If given step is initial axis position + */ + abstract public function isZeroStep( $step ); + + /** + * Add data for this axis + * + * @param array $values + * @return void + */ + abstract public function addData( array $values ); + + /** + * Calculate axis bounding values on base of the assigned values + * + * @abstract + * @access public + * @return void + */ + abstract public function calculateAxisBoundings(); + + /** + * Render the axis + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Boundings for the axis + * @return ezcGraphBoundings Remaining boundings + */ + public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings = null ) + { + $this->innerBoundings = $innerBoundings; + $startSpace = $this->axisSpace; + $endSpace = $this->outerAxisSpace === null ? $this->axisSpace : $this->outerAxisSpace; + + switch ( $this->position ) + { + case ezcGraph::TOP: + $start = new ezcGraphCoordinate( + ( $boundings->x1 - $boundings->x0 ) * $startSpace + + $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $startSpace ), + 0 + ); + $end = new ezcGraphCoordinate( + ( $boundings->x1 - $boundings->x0 ) * $endSpace + + $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $endSpace ), + $boundings->y1 - $boundings->y0 + ); + break; + case ezcGraph::BOTTOM: + $start = new ezcGraphCoordinate( + ( $boundings->x1 - $boundings->x0 ) * $startSpace + + $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $startSpace ), + $boundings->y1 - $boundings->y0 + ); + $end = new ezcGraphCoordinate( + ( $boundings->x1 - $boundings->x0 ) * $endSpace + + $this->nullPosition * ( $boundings->x1 - $boundings->x0 ) * ( 1 - 2 * $endSpace ), + 0 + ); + break; + case ezcGraph::LEFT: + $start = new ezcGraphCoordinate( + 0, + ( $boundings->y1 - $boundings->y0 ) * $startSpace + + $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $startSpace ) + ); + $end = new ezcGraphCoordinate( + $boundings->x1 - $boundings->x0, + ( $boundings->y1 - $boundings->y0 ) * $endSpace + + $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $endSpace ) + ); + break; + case ezcGraph::RIGHT: + $start = new ezcGraphCoordinate( + $boundings->x1 - $boundings->x0, + ( $boundings->y1 - $boundings->y0 ) * $startSpace + + $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $startSpace ) + ); + $end = new ezcGraphCoordinate( + 0, + ( $boundings->y1 - $boundings->y0 ) * $endSpace + + $this->nullPosition * ( $boundings->y1 - $boundings->y0 ) * ( 1 - 2 * $endSpace ) + ); + break; + } + + $renderer->drawAxis( + $boundings, + $start, + $end, + $this, + $this->axisLabelRenderer, + $innerBoundings + ); + + return $boundings; + } +} + +?> diff --git a/library/ezc/Graph/src/element/background.php b/library/ezc/Graph/src/element/background.php index 1534db504c..6db97c28b9 100644 --- a/library/ezc/Graph/src/element/background.php +++ b/library/ezc/Graph/src/element/background.php @@ -1,221 +1,221 @@ - - * $chart = new ezcGraphPieChart(); - * $chart->data['example'] = new ezcGraphArrayDataSet( array( - * 'Foo' => 23, - * 'Bar' => 42, - * ) ); - * - * $chart->background->image = 'background.png'; - * - * // Image would be repeated horizontal at the top of the background - * $chart->background->repeat = ezcGraph::HORIZONTAL; - * $chart->background->postion = ezcGraph::TOP; - * - * // Image would be placed once in the center - * $chart->background->repeat = ezcGraph::NO_REPEAT; // default; - * $chart->background->position = ezcGraph::CENTER | ezcGraph::MIDDLE; - * - * // Image would be repeated all over the chart, the position is irrelevant - * $chart->background->repeat = ezcGraph::HORIZONTAL | ezcGraph::VERTICAL; - * - * $graph->render( 400, 250, 'legend.svg' ); - * - * - * @property string $image - * Filename of the file to use for background - * @property int $repeat - * Defines how the background image gets repeated - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementBackground extends ezcGraphChartElement -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['image'] = false; - $this->properties['repeat'] = ezcGraph::NO_REPEAT; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'image': - // Check for existance of file - if ( !is_file( $propertyValue ) || !is_readable( $propertyValue ) ) - { - throw new ezcBaseFileNotFoundException( $propertyValue ); - } - - // Check for beeing an image file - $data = getImageSize( $propertyValue ); - if ( $data === false ) - { - throw new ezcGraphInvalidImageFileException( $propertyValue ); - } - - // SWF files are useless.. - if ( $data[2] === 4 ) - { - throw new ezcGraphInvalidImageFileException( 'We cant use SWF files like <' . $propertyValue . '>.' ); - } - - $this->properties['image'] = $propertyValue; - break; - case 'repeat': - if ( ( $propertyValue >= 0 ) && ( $propertyValue <= 3 ) ) - { - $this->properties['repeat'] = (int) $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 3' ); - } - break; - case 'position': - // Overwrite parent position setter, to be able to use - // combination of positions like - // ezcGraph::TOP | ezcGraph::CENTER - if ( is_int( $propertyValue ) ) - { - $this->properties['position'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'integer' ); - } - break; - case 'color': - // Use color as an alias to set background color for background - $this->__set( 'background', $propertyValue ); - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'color': - // Use color as an alias to set background color for background - return $this->properties['background']; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Set colors and border for this element - * - * Method is overwritten because we do not ant to apply the global padding - * and margin here. - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - $this->border = $palette->chartBorderColor; - $this->borderWidth = $palette->chartBorderWidth; - $this->background = $palette->chartBackground; - $this->padding = 0; - $this->margin = 0; - } - - /** - * Render the background - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Boundings - * @return ezcGraphBoundings Remaining boundings - */ - public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - $boundings = $renderer->drawBox( - $boundings, - $this->background, - $this->border, - $this->borderWidth, - $this->margin, - $this->padding - ); - - if ( $this->image === false ) - { - return $boundings; - } - - $renderer->drawBackgroundImage( - $boundings, - $this->image, - $this->position, - $this->repeat - ); - - return $boundings; - } -} - -?> + + * $chart = new ezcGraphPieChart(); + * $chart->data['example'] = new ezcGraphArrayDataSet( array( + * 'Foo' => 23, + * 'Bar' => 42, + * ) ); + * + * $chart->background->image = 'background.png'; + * + * // Image would be repeated horizontal at the top of the background + * $chart->background->repeat = ezcGraph::HORIZONTAL; + * $chart->background->postion = ezcGraph::TOP; + * + * // Image would be placed once in the center + * $chart->background->repeat = ezcGraph::NO_REPEAT; // default; + * $chart->background->position = ezcGraph::CENTER | ezcGraph::MIDDLE; + * + * // Image would be repeated all over the chart, the position is irrelevant + * $chart->background->repeat = ezcGraph::HORIZONTAL | ezcGraph::VERTICAL; + * + * $graph->render( 400, 250, 'legend.svg' ); + * + * + * @property string $image + * Filename of the file to use for background + * @property int $repeat + * Defines how the background image gets repeated + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementBackground extends ezcGraphChartElement +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['image'] = false; + $this->properties['repeat'] = ezcGraph::NO_REPEAT; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'image': + // Check for existance of file + if ( !is_file( $propertyValue ) || !is_readable( $propertyValue ) ) + { + throw new ezcBaseFileNotFoundException( $propertyValue ); + } + + // Check for beeing an image file + $data = getImageSize( $propertyValue ); + if ( $data === false ) + { + throw new ezcGraphInvalidImageFileException( $propertyValue ); + } + + // SWF files are useless.. + if ( $data[2] === 4 ) + { + throw new ezcGraphInvalidImageFileException( 'We cant use SWF files like <' . $propertyValue . '>.' ); + } + + $this->properties['image'] = $propertyValue; + break; + case 'repeat': + if ( ( $propertyValue >= 0 ) && ( $propertyValue <= 3 ) ) + { + $this->properties['repeat'] = (int) $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 3' ); + } + break; + case 'position': + // Overwrite parent position setter, to be able to use + // combination of positions like + // ezcGraph::TOP | ezcGraph::CENTER + if ( is_int( $propertyValue ) ) + { + $this->properties['position'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'integer' ); + } + break; + case 'color': + // Use color as an alias to set background color for background + $this->__set( 'background', $propertyValue ); + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'color': + // Use color as an alias to set background color for background + return $this->properties['background']; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Set colors and border for this element + * + * Method is overwritten because we do not ant to apply the global padding + * and margin here. + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + $this->border = $palette->chartBorderColor; + $this->borderWidth = $palette->chartBorderWidth; + $this->background = $palette->chartBackground; + $this->padding = 0; + $this->margin = 0; + } + + /** + * Render the background + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Boundings + * @return ezcGraphBoundings Remaining boundings + */ + public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + $boundings = $renderer->drawBox( + $boundings, + $this->background, + $this->border, + $this->borderWidth, + $this->margin, + $this->padding + ); + + if ( $this->image === false ) + { + return $boundings; + } + + $renderer->drawBackgroundImage( + $boundings, + $this->image, + $this->position, + $this->repeat + ); + + return $boundings; + } +} + +?> diff --git a/library/ezc/Graph/src/element/legend.php b/library/ezc/Graph/src/element/legend.php index 0cd78ab65e..c5a21123c1 100644 --- a/library/ezc/Graph/src/element/legend.php +++ b/library/ezc/Graph/src/element/legend.php @@ -1,339 +1,339 @@ - - * $chart->legend = false; - * - * - * The position of the legend in the chart can be influenced by the postion - * property, set to one of the position constants from the ezcGraph base class, - * like ezcGraph::BOTTOM, ezcGraph::LEFT, ezcGraph::RIGHT, ezcGraph::TOP. - * - * Depending on the position of the legend, either the $portraitSize (RIGHT, - * LEFT) or the $landscapeSize (TOP, BOTTOM) defines how much space will be - * aqquired for the legend. - * - * - * $graph = new ezcGraphPieChart(); - * $graph->data['example'] = new ezcGraphArrayDataSet( array( - * 'Foo' => 23, - * 'Bar' => 42, - * ) ); - * - * // Format the legend element - * $graph->legend->background = '#FFFFFF80'; - * - * // Place at the bottom of the chart, with a height of 5% of the remaining - * // chart space. - * $graph->legend->position = ezcGraph::BOTTOM; - * $graph->legend->landscapeSize = .05; - * - * $graph->render( 400, 250, 'legend.svg' ); - * - * - * @property float $portraitSize - * Size of a portrait style legend in percent of the size of the - * complete chart. - * @property float $landscapeSize - * Size of a landscape style legend in percent of the size of the - * complete chart. - * @property int $symbolSize - * Standard size of symbols and text in legends. - * @property float $minimumSymbolSize - * Scale symbol size up to to percent of complete legends size for - * very big legends. - * @property int $spacing - * Space between labels elements in pixel. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementLegend extends ezcGraphChartElement -{ - - /** - * Contains data which should be shown in the legend - * array( - * array( - * 'label' => (string) 'Label of data element', - * 'color' => (ezcGraphColor) $color, - * 'symbol' => (integer) ezcGraph::DIAMOND, - * ), - * ... - * ) - * - * @var array - */ - protected $labels; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['portraitSize'] = .2; - $this->properties['landscapeSize'] = .1; - $this->properties['symbolSize'] = 14; - $this->properties['padding'] = 1; - $this->properties['minimumSymbolSize'] = .05; - $this->properties['spacing'] = 2; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'padding': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['padding'] = (int) $propertyValue; - break; - case 'symbolSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['symbolSize'] = (int) $propertyValue; - break; - case 'landscapeSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); - } - - $this->properties['landscapeSize'] = (float) $propertyValue; - break; - case 'portraitSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); - } - - $this->properties['portraitSize'] = (float) $propertyValue; - break; - case 'minimumSymbolSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); - } - - $this->properties['minimumSymbolSize'] = (float) $propertyValue; - break; - case 'spacing': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['spacing'] = (int) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'labels': - return $this->labels; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Generate legend from several datasets with on entry per dataset - * - * @param ezcGraphChartDataContainer $datasets - * @return void - */ - public function generateFromDataSets( ezcGraphChartDataContainer $datasets ) - { - $this->labels = array(); - foreach ( $datasets as $dataset ) - { - $this->labels[] = array( - 'label' => $dataset->label->default, - 'url' => $dataset->url->default, - 'color' => $dataset->color->default, - 'symbol' => ( $dataset->symbol->default === null ? - ezcGraph::NO_SYMBOL : - $dataset->symbol->default ), - ); - } - } - - /** - * Generate legend from single dataset with on entry per data element - * - * @param ezcGraphDataSet $dataset - * @return void - */ - public function generateFromDataSet( ezcGraphDataSet $dataset ) - { - $this->labels = array(); - foreach ( $dataset as $label => $data ) - { - $this->labels[] = array( - 'label' => $label, - 'url' => $dataset->url[$label], - 'color' => $dataset->color[$label], - 'symbol' => ( $dataset->symbol[$label] === null ? - ezcGraph::NO_SYMBOL : - $dataset->symbol[$label] ), - ); - } - } - - /** - * Calculated boundings needed for the legend. - * - * Uses the position and the configured horizontal or vertical size of a - * legend to calculate the boundings for the legend. - * - * @param ezcGraphBoundings $boundings Avalable boundings - * @return ezcGraphBoundings Remaining boundings - */ - protected function calculateBoundings( ezcGraphBoundings $boundings ) - { - $this->properties['boundings'] = clone $boundings; - - switch ( $this->position ) - { - case ezcGraph::LEFT: - $size = ( $boundings->width ) * $this->portraitSize; - - $boundings->x0 += $size; - $this->boundings->x1 = $boundings->x0; - break; - case ezcGraph::RIGHT: - $size = ( $boundings->width ) * $this->portraitSize; - - $boundings->x1 -= $size; - $this->boundings->x0 = $boundings->x1; - break; - case ezcGraph::TOP: - $size = ( $boundings->height ) * $this->landscapeSize; - - $boundings->y0 += $size; - $this->boundings->y1 = $boundings->y0; - break; - case ezcGraph::BOTTOM: - $size = ( $boundings->height ) * $this->landscapeSize; - - $boundings->y1 -= $size; - $this->boundings->y0 = $boundings->y1; - break; - } - - return $boundings; - } - - /** - * Render a legend - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Boundings for the axis - * @return ezcGraphBoundings Remaining boundings - */ - public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - $boundings = $this->calculateBoundings( $boundings ); - - if ( $this->position === ezcGraph::LEFT || $this->position === ezcGraph::RIGHT ) - { - $type = ezcGraph::VERTICAL; - } - else - { - $type = ezcGraph::HORIZONTAL; - } - - // Render standard elements - $this->properties['boundings'] = $renderer->drawBox( - $this->properties['boundings'], - $this->properties['background'], - $this->properties['border'], - $this->properties['borderWidth'], - $this->properties['margin'], - $this->properties['padding'], - $this->properties['title'], - $this->getTitleSize( $this->properties['boundings'], $type ) - ); - - // Render legend - $renderer->drawLegend( - $this->boundings, - $this, - $type - ); - - return $boundings; - } -} - -?> + + * $chart->legend = false; + * + * + * The position of the legend in the chart can be influenced by the postion + * property, set to one of the position constants from the ezcGraph base class, + * like ezcGraph::BOTTOM, ezcGraph::LEFT, ezcGraph::RIGHT, ezcGraph::TOP. + * + * Depending on the position of the legend, either the $portraitSize (RIGHT, + * LEFT) or the $landscapeSize (TOP, BOTTOM) defines how much space will be + * aqquired for the legend. + * + * + * $graph = new ezcGraphPieChart(); + * $graph->data['example'] = new ezcGraphArrayDataSet( array( + * 'Foo' => 23, + * 'Bar' => 42, + * ) ); + * + * // Format the legend element + * $graph->legend->background = '#FFFFFF80'; + * + * // Place at the bottom of the chart, with a height of 5% of the remaining + * // chart space. + * $graph->legend->position = ezcGraph::BOTTOM; + * $graph->legend->landscapeSize = .05; + * + * $graph->render( 400, 250, 'legend.svg' ); + * + * + * @property float $portraitSize + * Size of a portrait style legend in percent of the size of the + * complete chart. + * @property float $landscapeSize + * Size of a landscape style legend in percent of the size of the + * complete chart. + * @property int $symbolSize + * Standard size of symbols and text in legends. + * @property float $minimumSymbolSize + * Scale symbol size up to to percent of complete legends size for + * very big legends. + * @property int $spacing + * Space between labels elements in pixel. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementLegend extends ezcGraphChartElement +{ + + /** + * Contains data which should be shown in the legend + * array( + * array( + * 'label' => (string) 'Label of data element', + * 'color' => (ezcGraphColor) $color, + * 'symbol' => (integer) ezcGraph::DIAMOND, + * ), + * ... + * ) + * + * @var array + */ + protected $labels; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['portraitSize'] = .2; + $this->properties['landscapeSize'] = .1; + $this->properties['symbolSize'] = 14; + $this->properties['padding'] = 1; + $this->properties['minimumSymbolSize'] = .05; + $this->properties['spacing'] = 2; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'padding': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['padding'] = (int) $propertyValue; + break; + case 'symbolSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['symbolSize'] = (int) $propertyValue; + break; + case 'landscapeSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); + } + + $this->properties['landscapeSize'] = (float) $propertyValue; + break; + case 'portraitSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); + } + + $this->properties['portraitSize'] = (float) $propertyValue; + break; + case 'minimumSymbolSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' ); + } + + $this->properties['minimumSymbolSize'] = (float) $propertyValue; + break; + case 'spacing': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['spacing'] = (int) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'labels': + return $this->labels; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Generate legend from several datasets with on entry per dataset + * + * @param ezcGraphChartDataContainer $datasets + * @return void + */ + public function generateFromDataSets( ezcGraphChartDataContainer $datasets ) + { + $this->labels = array(); + foreach ( $datasets as $dataset ) + { + $this->labels[] = array( + 'label' => $dataset->label->default, + 'url' => $dataset->url->default, + 'color' => $dataset->color->default, + 'symbol' => ( $dataset->symbol->default === null ? + ezcGraph::NO_SYMBOL : + $dataset->symbol->default ), + ); + } + } + + /** + * Generate legend from single dataset with on entry per data element + * + * @param ezcGraphDataSet $dataset + * @return void + */ + public function generateFromDataSet( ezcGraphDataSet $dataset ) + { + $this->labels = array(); + foreach ( $dataset as $label => $data ) + { + $this->labels[] = array( + 'label' => $label, + 'url' => $dataset->url[$label], + 'color' => $dataset->color[$label], + 'symbol' => ( $dataset->symbol[$label] === null ? + ezcGraph::NO_SYMBOL : + $dataset->symbol[$label] ), + ); + } + } + + /** + * Calculated boundings needed for the legend. + * + * Uses the position and the configured horizontal or vertical size of a + * legend to calculate the boundings for the legend. + * + * @param ezcGraphBoundings $boundings Avalable boundings + * @return ezcGraphBoundings Remaining boundings + */ + protected function calculateBoundings( ezcGraphBoundings $boundings ) + { + $this->properties['boundings'] = clone $boundings; + + switch ( $this->position ) + { + case ezcGraph::LEFT: + $size = ( $boundings->width ) * $this->portraitSize; + + $boundings->x0 += $size; + $this->boundings->x1 = $boundings->x0; + break; + case ezcGraph::RIGHT: + $size = ( $boundings->width ) * $this->portraitSize; + + $boundings->x1 -= $size; + $this->boundings->x0 = $boundings->x1; + break; + case ezcGraph::TOP: + $size = ( $boundings->height ) * $this->landscapeSize; + + $boundings->y0 += $size; + $this->boundings->y1 = $boundings->y0; + break; + case ezcGraph::BOTTOM: + $size = ( $boundings->height ) * $this->landscapeSize; + + $boundings->y1 -= $size; + $this->boundings->y0 = $boundings->y1; + break; + } + + return $boundings; + } + + /** + * Render a legend + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Boundings for the axis + * @return ezcGraphBoundings Remaining boundings + */ + public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + $boundings = $this->calculateBoundings( $boundings ); + + if ( $this->position === ezcGraph::LEFT || $this->position === ezcGraph::RIGHT ) + { + $type = ezcGraph::VERTICAL; + } + else + { + $type = ezcGraph::HORIZONTAL; + } + + // Render standard elements + $this->properties['boundings'] = $renderer->drawBox( + $this->properties['boundings'], + $this->properties['background'], + $this->properties['border'], + $this->properties['borderWidth'], + $this->properties['margin'], + $this->properties['padding'], + $this->properties['title'], + $this->getTitleSize( $this->properties['boundings'], $type ) + ); + + // Render legend + $renderer->drawLegend( + $this->boundings, + $this, + $type + ); + + return $boundings; + } +} + +?> diff --git a/library/ezc/Graph/src/element/text.php b/library/ezc/Graph/src/element/text.php index 18044b380b..c67f7d7ab3 100644 --- a/library/ezc/Graph/src/element/text.php +++ b/library/ezc/Graph/src/element/text.php @@ -1,150 +1,150 @@ - - * $chart = new ezcGraphPieChart(); - * $chart->data['example'] = new ezcGraphArrayDataSet( array( - * 'Foo' => 23, - * 'Bar' => 42, - * ) ); - * - * $chart->title = 'Some pie chart'; - * - * // Use at maximum 5% of the chart height for the title. - * $chart->title->maxHeight = .05; - * - * $graph->render( 400, 250, 'title.svg' ); - * - * - * @property float $maxHeight - * Maximum percent of bounding used to display the text. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphChartElementText extends ezcGraphChartElement -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['maxHeight'] = .1; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'maxHeight': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['maxHeight'] = (float) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } - - /** - * Render the text - * - * @param ezcGraphRenderer $renderer Renderer - * @param ezcGraphBoundings $boundings Boundings for the axis - * @return ezcGraphBoundings Remaining boundings - */ - public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) - { - $height = (int) min( - round( $this->properties['maxHeight'] * ( $boundings->y1 - $boundings->y0 ) ), - $this->properties['font']->maxFontSize + $this->padding * 2 + $this->margin * 2 - ); - - switch ( $this->properties['position'] ) - { - case ezcGraph::TOP: - $textBoundings = new ezcGraphBoundings( - $boundings->x0, - $boundings->y0, - $boundings->x1, - $boundings->y0 + $height - ); - $boundings->y0 += $height + $this->properties['margin']; - break; - case ezcGraph::BOTTOM: - $textBoundings = new ezcGraphBoundings( - $boundings->x0, - $boundings->y1 - $height, - $boundings->x1, - $boundings->y1 - ); - $boundings->y1 -= $height + $this->properties['margin']; - break; - } - - $textBoundings = $renderer->drawBox( - $textBoundings, - $this->properties['background'], - $this->properties['border'], - $this->properties['borderWidth'], - $this->properties['margin'], - $this->properties['padding'] - ); - - $renderer->drawText( - $textBoundings, - $this->properties['title'], - ezcGraph::CENTER | ezcGraph::MIDDLE - ); - - return $boundings; - } -} - -?> + + * $chart = new ezcGraphPieChart(); + * $chart->data['example'] = new ezcGraphArrayDataSet( array( + * 'Foo' => 23, + * 'Bar' => 42, + * ) ); + * + * $chart->title = 'Some pie chart'; + * + * // Use at maximum 5% of the chart height for the title. + * $chart->title->maxHeight = .05; + * + * $graph->render( 400, 250, 'title.svg' ); + * + * + * @property float $maxHeight + * Maximum percent of bounding used to display the text. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphChartElementText extends ezcGraphChartElement +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['maxHeight'] = .1; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'maxHeight': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['maxHeight'] = (float) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } + + /** + * Render the text + * + * @param ezcGraphRenderer $renderer Renderer + * @param ezcGraphBoundings $boundings Boundings for the axis + * @return ezcGraphBoundings Remaining boundings + */ + public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ) + { + $height = (int) min( + round( $this->properties['maxHeight'] * ( $boundings->y1 - $boundings->y0 ) ), + $this->properties['font']->maxFontSize + $this->padding * 2 + $this->margin * 2 + ); + + switch ( $this->properties['position'] ) + { + case ezcGraph::TOP: + $textBoundings = new ezcGraphBoundings( + $boundings->x0, + $boundings->y0, + $boundings->x1, + $boundings->y0 + $height + ); + $boundings->y0 += $height + $this->properties['margin']; + break; + case ezcGraph::BOTTOM: + $textBoundings = new ezcGraphBoundings( + $boundings->x0, + $boundings->y1 - $height, + $boundings->x1, + $boundings->y1 + ); + $boundings->y1 -= $height + $this->properties['margin']; + break; + } + + $textBoundings = $renderer->drawBox( + $textBoundings, + $this->properties['background'], + $this->properties['border'], + $this->properties['borderWidth'], + $this->properties['margin'], + $this->properties['padding'] + ); + + $renderer->drawText( + $textBoundings, + $this->properties['title'], + ezcGraph::CENTER | ezcGraph::MIDDLE + ); + + return $boundings; + } +} + +?> diff --git a/library/ezc/Graph/src/exceptions/date_parsing.php b/library/ezc/Graph/src/exceptions/date_parsing.php index 3653155ef4..940ef0fee0 100644 --- a/library/ezc/Graph/src/exceptions/date_parsing.php +++ b/library/ezc/Graph/src/exceptions/date_parsing.php @@ -1,33 +1,33 @@ - + diff --git a/library/ezc/Graph/src/exceptions/exception.php b/library/ezc/Graph/src/exceptions/exception.php index ce238d78f5..337148aec8 100644 --- a/library/ezc/Graph/src/exceptions/exception.php +++ b/library/ezc/Graph/src/exceptions/exception.php @@ -1,20 +1,20 @@ - + diff --git a/library/ezc/Graph/src/exceptions/flash_bitmap_type.php b/library/ezc/Graph/src/exceptions/flash_bitmap_type.php index f1b4b3d1ca..628ff7db48 100644 --- a/library/ezc/Graph/src/exceptions/flash_bitmap_type.php +++ b/library/ezc/Graph/src/exceptions/flash_bitmap_type.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/font_rendering.php b/library/ezc/Graph/src/exceptions/font_rendering.php index 2957b57185..c19ff00ae1 100644 --- a/library/ezc/Graph/src/exceptions/font_rendering.php +++ b/library/ezc/Graph/src/exceptions/font_rendering.php @@ -1,40 +1,40 @@ - + diff --git a/library/ezc/Graph/src/exceptions/font_type.php b/library/ezc/Graph/src/exceptions/font_type.php index ff9fb8cff9..c8ca94198d 100644 --- a/library/ezc/Graph/src/exceptions/font_type.php +++ b/library/ezc/Graph/src/exceptions/font_type.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/incompatible_driver.php b/library/ezc/Graph/src/exceptions/incompatible_driver.php index 155ca30cdb..efd585f184 100644 --- a/library/ezc/Graph/src/exceptions/incompatible_driver.php +++ b/library/ezc/Graph/src/exceptions/incompatible_driver.php @@ -1,34 +1,34 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_assignement.php b/library/ezc/Graph/src/exceptions/invalid_assignement.php index b98257dfe2..fd3e509a29 100644 --- a/library/ezc/Graph/src/exceptions/invalid_assignement.php +++ b/library/ezc/Graph/src/exceptions/invalid_assignement.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_data.php b/library/ezc/Graph/src/exceptions/invalid_data.php index 56e41babee..0c3575592d 100644 --- a/library/ezc/Graph/src/exceptions/invalid_data.php +++ b/library/ezc/Graph/src/exceptions/invalid_data.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_data_source.php b/library/ezc/Graph/src/exceptions/invalid_data_source.php index b6af1b28e9..b2c6d48b57 100644 --- a/library/ezc/Graph/src/exceptions/invalid_data_source.php +++ b/library/ezc/Graph/src/exceptions/invalid_data_source.php @@ -1,33 +1,33 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_dimensions.php b/library/ezc/Graph/src/exceptions/invalid_dimensions.php index aeec32c013..73414e48c2 100644 --- a/library/ezc/Graph/src/exceptions/invalid_dimensions.php +++ b/library/ezc/Graph/src/exceptions/invalid_dimensions.php @@ -1,35 +1,35 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_display_type.php b/library/ezc/Graph/src/exceptions/invalid_display_type.php index dc5b2e64d1..568ed9a7de 100644 --- a/library/ezc/Graph/src/exceptions/invalid_display_type.php +++ b/library/ezc/Graph/src/exceptions/invalid_display_type.php @@ -1,46 +1,46 @@ - 'Pie', - ezcGraph::LINE => 'Line', - ezcGraph::BAR => 'Bar', - ); - - if ( isset( $chartTypeNames[$type] ) ) - { - $chartTypeName = $chartTypeNames[$type]; - } - else - { - $chartTypeName = 'Unknown'; - } - - parent::__construct( "Invalid data set display type '$type' ('$chartTypeName') for current chart." ); - } -} - -?> + 'Pie', + ezcGraph::LINE => 'Line', + ezcGraph::BAR => 'Bar', + ); + + if ( isset( $chartTypeNames[$type] ) ) + { + $chartTypeName = $chartTypeNames[$type]; + } + else + { + $chartTypeName = 'Unknown'; + } + + parent::__construct( "Invalid data set display type '$type' ('$chartTypeName') for current chart." ); + } +} + +?> diff --git a/library/ezc/Graph/src/exceptions/invalid_id.php b/library/ezc/Graph/src/exceptions/invalid_id.php index 60972f6677..f59ad52452 100644 --- a/library/ezc/Graph/src/exceptions/invalid_id.php +++ b/library/ezc/Graph/src/exceptions/invalid_id.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_image_file.php b/library/ezc/Graph/src/exceptions/invalid_image_file.php index 4dc51a7cf0..c1982fa9bc 100644 --- a/library/ezc/Graph/src/exceptions/invalid_image_file.php +++ b/library/ezc/Graph/src/exceptions/invalid_image_file.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_keys.php b/library/ezc/Graph/src/exceptions/invalid_keys.php index 829d9f2be9..f4630f81f8 100644 --- a/library/ezc/Graph/src/exceptions/invalid_keys.php +++ b/library/ezc/Graph/src/exceptions/invalid_keys.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/invalid_step_size.php b/library/ezc/Graph/src/exceptions/invalid_step_size.php index 7ae24fe83d..1c902b6939 100644 --- a/library/ezc/Graph/src/exceptions/invalid_step_size.php +++ b/library/ezc/Graph/src/exceptions/invalid_step_size.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/no_data.php b/library/ezc/Graph/src/exceptions/no_data.php index 47f24bc5bd..9cc74c4e93 100644 --- a/library/ezc/Graph/src/exceptions/no_data.php +++ b/library/ezc/Graph/src/exceptions/no_data.php @@ -1,30 +1,30 @@ - + diff --git a/library/ezc/Graph/src/exceptions/no_such_data.php b/library/ezc/Graph/src/exceptions/no_such_data.php index 35bcb8bd82..fc70e80def 100644 --- a/library/ezc/Graph/src/exceptions/no_such_data.php +++ b/library/ezc/Graph/src/exceptions/no_such_data.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/no_such_dataset.php b/library/ezc/Graph/src/exceptions/no_such_dataset.php index e37fdf7253..9ecb5acc18 100644 --- a/library/ezc/Graph/src/exceptions/no_such_dataset.php +++ b/library/ezc/Graph/src/exceptions/no_such_dataset.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/no_such_element.php b/library/ezc/Graph/src/exceptions/no_such_element.php index ee9d917428..34106e1e24 100644 --- a/library/ezc/Graph/src/exceptions/no_such_element.php +++ b/library/ezc/Graph/src/exceptions/no_such_element.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/not_rendered.php b/library/ezc/Graph/src/exceptions/not_rendered.php index 64a5a34f54..e3a8e4d24a 100644 --- a/library/ezc/Graph/src/exceptions/not_rendered.php +++ b/library/ezc/Graph/src/exceptions/not_rendered.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/out_of_boundings.php b/library/ezc/Graph/src/exceptions/out_of_boundings.php index 8b7338c2f0..e18f16c324 100644 --- a/library/ezc/Graph/src/exceptions/out_of_boundings.php +++ b/library/ezc/Graph/src/exceptions/out_of_boundings.php @@ -1,35 +1,35 @@ - + diff --git a/library/ezc/Graph/src/exceptions/out_of_logarithmical_boundings.php b/library/ezc/Graph/src/exceptions/out_of_logarithmical_boundings.php index 48cd1d56ac..a9b46ded5d 100644 --- a/library/ezc/Graph/src/exceptions/out_of_logarithmical_boundings.php +++ b/library/ezc/Graph/src/exceptions/out_of_logarithmical_boundings.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/reducement_failed.php b/library/ezc/Graph/src/exceptions/reducement_failed.php index b7aa617b63..40491486d4 100644 --- a/library/ezc/Graph/src/exceptions/reducement_failed.php +++ b/library/ezc/Graph/src/exceptions/reducement_failed.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/too_many_datasets.php b/library/ezc/Graph/src/exceptions/too_many_datasets.php index ce64d18953..9829511745 100644 --- a/library/ezc/Graph/src/exceptions/too_many_datasets.php +++ b/library/ezc/Graph/src/exceptions/too_many_datasets.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/unknown_color_definition.php b/library/ezc/Graph/src/exceptions/unknown_color_definition.php index 55dbb2eea8..46ad0b95d6 100644 --- a/library/ezc/Graph/src/exceptions/unknown_color_definition.php +++ b/library/ezc/Graph/src/exceptions/unknown_color_definition.php @@ -1,32 +1,32 @@ - + diff --git a/library/ezc/Graph/src/exceptions/unregular_steps.php b/library/ezc/Graph/src/exceptions/unregular_steps.php index 34dfc8b297..27c7f4cb00 100644 --- a/library/ezc/Graph/src/exceptions/unregular_steps.php +++ b/library/ezc/Graph/src/exceptions/unregular_steps.php @@ -1,31 +1,31 @@ - + diff --git a/library/ezc/Graph/src/exceptions/unsupported_image_type.php b/library/ezc/Graph/src/exceptions/unsupported_image_type.php index 0b85ca6151..cb734e9897 100644 --- a/library/ezc/Graph/src/exceptions/unsupported_image_type.php +++ b/library/ezc/Graph/src/exceptions/unsupported_image_type.php @@ -1,61 +1,61 @@ - 'GIF', - 2 => 'Jpeg', - 3 => 'PNG', - 4 => 'SWF', - 5 => 'PSD', - 6 => 'BMP', - 7 => 'TIFF (intel)', - 8 => 'TIFF (motorola)', - 9 => 'JPC', - 10 => 'JP2', - 11 => 'JPX', - 12 => 'JB2', - 13 => 'SWC', - 14 => 'IFF', - 15 => 'WBMP', - 16 => 'XBM', - - ); - - if ( isset( $typeName[$type] ) ) - { - $type = $typeName[$type]; - } - else - { - $type = 'Unknown'; - } - - parent::__construct( "Unsupported image format '{$type}'." ); - } -} - -?> + 'GIF', + 2 => 'Jpeg', + 3 => 'PNG', + 4 => 'SWF', + 5 => 'PSD', + 6 => 'BMP', + 7 => 'TIFF (intel)', + 8 => 'TIFF (motorola)', + 9 => 'JPC', + 10 => 'JP2', + 11 => 'JPX', + 12 => 'JB2', + 13 => 'SWC', + 14 => 'IFF', + 15 => 'WBMP', + 16 => 'XBM', + + ); + + if ( isset( $typeName[$type] ) ) + { + $type = $typeName[$type]; + } + else + { + $type = 'Unknown'; + } + + parent::__construct( "Unsupported image format '{$type}'." ); + } +} + +?> diff --git a/library/ezc/Graph/src/graph.php b/library/ezc/Graph/src/graph.php index 13823ad422..4c191bf5bb 100644 --- a/library/ezc/Graph/src/graph.php +++ b/library/ezc/Graph/src/graph.php @@ -1,147 +1,147 @@ - + diff --git a/library/ezc/Graph/src/interfaces/axis_label_renderer.php b/library/ezc/Graph/src/interfaces/axis_label_renderer.php index e94332df61..3bb2442e75 100644 --- a/library/ezc/Graph/src/interfaces/axis_label_renderer.php +++ b/library/ezc/Graph/src/interfaces/axis_label_renderer.php @@ -1,557 +1,557 @@ -properties['majorStepCount'] = false; - $this->properties['minorStepCount'] = false; - $this->properties['majorStepSize'] = 3; - $this->properties['minorStepSize'] = 1; - $this->properties['innerStep'] = true; - $this->properties['outerStep'] = false; - $this->properties['outerGrid'] = false; - $this->properties['showLabels'] = true; - $this->properties['labelPadding'] = 2; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'driver': - if ( $propertyValue instanceof ezcGraphDriver ) - { - $this->properties['driver'] = $propertyValue; - } - else - { - throw new ezcGraphInvalidDriverException( $propertyValue ); - } - break; - case 'majorStepCount': - if ( ( $propertyValue !== false ) && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['majorStepCount'] = (int) $propertyValue; - break; - case 'minorStepCount': - if ( ( $propertyValue !== false ) && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['minorStepCount'] = (int) $propertyValue; - break; - case 'majorStepSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['majorStepSize'] = (int) $propertyValue; - break; - case 'minorStepSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['minorStepSize'] = (int) $propertyValue; - break; - case 'innerStep': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['innerStep'] = (bool) $propertyValue; - break; - case 'outerStep': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['outerStep'] = (bool) $propertyValue; - break; - case 'outerGrid': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['outerGrid'] = (bool) $propertyValue; - break; - case 'showLabels': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['showLabels'] = (bool) $propertyValue; - break; - case 'labelPadding': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['labelPadding'] = (int) $propertyValue; - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - - /** - * Checks for the cutting point of two lines. - * - * The lines are given by a start position and the direction of the line, - * both as instances of {@link ezcGraphCoordinate}. If no cutting point - * could be calculated, because the lines are parallel the function will - * return false. Otherwise the factor returned can be used to calculate the - * cutting point using the following equatation: - * point = $aStart + $factor * $aDir; - * - * We return the factor instead of the resulting point because it can be - * easily determined from the factor if the cutting point is in "behind" - * the line starting point, or if the distance to the cutting point is - * bigger then the direction vector is long ( $factor > 1 ). - * - * @param ezcGraphCoordinate $aStart - * @param ezcGraphCoordinate $aDir - * @param ezcGraphCoordinate $bStart - * @param ezcGraphCoordinate $bDir - * @return mixed - */ - public function determineLineCuttingPoint( ezcGraphCoordinate $aStart, ezcGraphCoordinate $aDir, ezcGraphCoordinate $bStart, ezcGraphCoordinate $bDir ) - { - // Check if lines are parallel - if ( ( ( abs( $aDir->x ) < .000001 ) && ( abs( $bDir->x ) < .000001 ) ) || - ( ( abs( $aDir->y ) < .000001 ) && ( abs( $bDir->y ) < .000001 ) ) || - ( ( abs( $aDir->x * $bDir->x * $aDir->y * $bDir->y ) > .000001 ) && - ( abs( ( $aDir->x / $aDir->y ) - ( $bDir->x / $bDir->y ) ) < .000001 ) - ) - ) - { - return false; - } - - // Use ? : to prevent division by zero - $denominator = - ( abs( $aDir->y ) > .000001 ? $bDir->y / $aDir->y : .0 ) - - ( abs( $aDir->x ) > .000001 ? $bDir->x / $aDir->x : .0 ); - - // Solve equatation - if ( abs( $denominator ) < .000001 ) - { - return - ( - ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) - - ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) - - ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) + - ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 ) - ); - } - else - { - return - ( - ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) - - ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) - - ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) + - ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 ) - ) / $denominator; - } - } - - /** - * Draw single step on a axis - * - * Draws a step on a axis at the current position - * - * @param ezcGraphRenderer $renderer Renderer to draw the step with - * @param ezcGraphCoordinate $position Position of step - * @param ezcGraphCoordinate $direction Direction of axis - * @param int $axisPosition Position of axis - * @param int $size Step size - * @param ezcGraphColor $color Color of axis - * @return void - */ - public function drawStep( ezcGraphRenderer $renderer, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, $axisPosition, $size, ezcGraphColor $color ) - { - if ( ! ( $this->innerStep || $this->outerStep ) ) - { - return false; - } - - $drawStep = false; - if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) || - ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->outerStep ) || - ( ( $axisPosition === ezcGraph::TOP ) && $this->innerStep ) || - ( ( $axisPosition === ezcGraph::RIGHT ) && $this->outerStep ) || - ( ( $axisPosition === ezcGraph::LEFT ) && $this->innerStep ) ) - { - // Turn direction vector to left by 90 degrees and multiply - // with major step size - $stepStart = new ezcGraphCoordinate( - $position->x + $direction->y * $size, - $position->y - $direction->x * $size - ); - $drawStep = true; - } - else - { - $stepStart = $position; - } - - if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) || - ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->innerStep ) || - ( ( $axisPosition === ezcGraph::TOP ) && $this->outerStep ) || - ( ( $axisPosition === ezcGraph::RIGHT ) && $this->innerStep ) || - ( ( $axisPosition === ezcGraph::LEFT ) && $this->outerStep ) ) - { - // Turn direction vector to right by 90 degrees and multiply - // with major step size - $stepEnd = new ezcGraphCoordinate( - $position->x - $direction->y * $size, - $position->y + $direction->x * $size - ); - $drawStep = true; - } - else - { - $stepEnd = $position; - } - - if ( $drawStep ) - { - $renderer->drawStepLine( - $stepStart, - $stepEnd, - $color - ); - } - } - - /** - * Draw non-rectangular grid lines grid - * - * Draws a grid line at the current position, for non-rectangular axis. - * - * @param ezcGraphRenderer $renderer Renderer to draw the grid with - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $position Position of step - * @param ezcGraphCoordinate $direction Direction of axis - * @param ezcGraphColor $color Color of axis - * @return void - */ - protected function drawNonRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) - { - // Direction of grid line is direction of axis turned right by 90 - // degrees - $gridDirection = new ezcGraphCoordinate( - $direction->y, - - $direction->x - ); - - $cuttingPoints = array(); - foreach ( array( // Bounding lines - array( - 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - 'dir' => new ezcGraphCoordinate( 0, $boundings->y1 - $boundings->y0 ) - ), - array( - 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - 'dir' => new ezcGraphCoordinate( $boundings->x1 - $boundings->x0, 0 ) - ), - array( - 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - 'dir' => new ezcGraphCoordinate( 0, $boundings->y0 - $boundings->y1 ) - ), - array( - 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - 'dir' => new ezcGraphCoordinate( $boundings->x0 - $boundings->x1, 0 ) - ), - ) as $boundingLine ) - { - // Test for cutting points with bounding lines, where cutting - // position is between 0 and 1, which means, that the line is hit - // on the bounding box rectangle. Use these points as a start and - // ending point for the grid lines. There should *always* be - // exactly two points returned. - $cuttingPosition = $this->determineLineCuttingPoint( - $boundingLine['start'], - $boundingLine['dir'], - $position, - $gridDirection - ); - - if ( $cuttingPosition === false ) - { - continue; - } - - $cuttingPosition = abs( $cuttingPosition ); - - if ( ( $cuttingPosition >= 0 ) && - ( $cuttingPosition <= 1 ) ) - { - $cuttingPoints[] = new ezcGraphCoordinate( - $boundingLine['start']->x + $cuttingPosition * $boundingLine['dir']->x, - $boundingLine['start']->y + $cuttingPosition * $boundingLine['dir']->y - ); - } - } - - if ( count( $cuttingPoints ) < 2 ) - { - // This should not happpen - return false; - } - - // Finally draw grid line - $renderer->drawGridLine( - $cuttingPoints[0], - $cuttingPoints[1], - $color - ); - } - - /** - * Draw rectangular grid - * - * Draws a grid line at the current position for rectangular directed axis. - * - * Method special for rectangularly directed axis to minimize the floating - * point calculation inaccuracies. Those are not necessary for rectangles, - * while for non-rectangular directed axis. - * - * @param ezcGraphRenderer $renderer Renderer to draw the grid with - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $position Position of step - * @param ezcGraphCoordinate $direction Direction of axis - * @param ezcGraphColor $color Color of axis - * @return void - */ - protected function drawRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) - { - if ( abs( $direction->x ) < .00001 ) - { - $renderer->drawGridLine( - new ezcGraphCoordinate( - $boundings->x0, - $position->y - ), - new ezcGraphCoordinate( - $boundings->x1, - $position->y - ), - $color - ); - } - else - { - $renderer->drawGridLine( - new ezcGraphCoordinate( - $position->x, - $boundings->y0 - ), - new ezcGraphCoordinate( - $position->x, - $boundings->y1 - ), - $color - ); - } - } - - /** - * Draw grid - * - * Draws a grid line at the current position - * - * @param ezcGraphRenderer $renderer Renderer to draw the grid with - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $position Position of step - * @param ezcGraphCoordinate $direction Direction of axis - * @param ezcGraphColor $color Color of axis - * @return void - */ - protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) - { - // Check if the axis direction is rectangular - if ( ( abs( $direction->x ) < .00001 ) || - ( abs( $direction->y ) < .00001 ) ) - { - return $this->drawRectangularGrid( $renderer, $boundings, $position, $direction, $color ); - } - else - { - return $this->drawNonRectangularGrid( $renderer, $boundings, $position, $direction, $color ); - } - } - - /** - * Modify chart boundings - * - * Optionally modify boundings of chart data - * - * @param ezcGraphBoundings $boundings Current boundings of chart - * @param ezcGraphCoordinate $direction Direction of the current axis - * @return ezcGraphBoundings Modified boundings - */ - public function modifyChartBoundings( ezcGraphBoundings $boundings, ezcGraphCoordinate $direction ) - { - return $boundings; - } - - /** - * Modify chart data position - * - * Optionally additionally modify the coodinate of a data point - * - * @param ezcGraphCoordinate $coordinate Data point coordinate - * @return ezcGraphCoordinate Modified coordinate - */ - public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) - { - return $coordinate; - } - - /** - * Get axis space values - * - * Get axis space values, depending on passed parameters. If - * $innerBoundings is given it will be used to caclulat the axis spaces - * available for label rendering. If not given the legacy method will be - * used, which uses the xAxisSpace and yAxisSpace values calcualted by the - * renderer. - * - * Returns an array( $xSpace, $ySpace ), containing the irespective size in - * pixels. Additionally calculates the grid boundings passed by reference. - * - * @param ezcGraphRenderer $renderer - * @param ezcGraphBoundings $boundings - * @param mixed $innerBoundings - * @return array - */ - protected function getAxisSpace( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis, $innerBoundings, &$gridBoundings ) - { - if ( $innerBoundings !== null ) - { - $gridBoundings = clone $innerBoundings; - $xSpace = abs( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 - $boundings->x0 : $boundings->x1 - $innerBoundings->x1 ); - $ySpace = abs( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 - $boundings->y0 : $boundings->y1 - $innerBoundings->y1 ); - } - else - { - $gridBoundings = new ezcGraphBoundings( - $boundings->x0 + ( $xSpace = abs( $renderer->xAxisSpace ) ), - $boundings->y0 + ( $ySpace = abs( $renderer->yAxisSpace ) ), - $boundings->x1 - $xSpace, - $boundings->y1 - $ySpace - ); - } - - if ( $this->outerGrid ) - { - $gridBoundings = $boundings; - } - - return array( $xSpace, $ySpace ); - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - abstract public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis - ); -} - -?> +properties['majorStepCount'] = false; + $this->properties['minorStepCount'] = false; + $this->properties['majorStepSize'] = 3; + $this->properties['minorStepSize'] = 1; + $this->properties['innerStep'] = true; + $this->properties['outerStep'] = false; + $this->properties['outerGrid'] = false; + $this->properties['showLabels'] = true; + $this->properties['labelPadding'] = 2; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'driver': + if ( $propertyValue instanceof ezcGraphDriver ) + { + $this->properties['driver'] = $propertyValue; + } + else + { + throw new ezcGraphInvalidDriverException( $propertyValue ); + } + break; + case 'majorStepCount': + if ( ( $propertyValue !== false ) && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['majorStepCount'] = (int) $propertyValue; + break; + case 'minorStepCount': + if ( ( $propertyValue !== false ) && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['minorStepCount'] = (int) $propertyValue; + break; + case 'majorStepSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['majorStepSize'] = (int) $propertyValue; + break; + case 'minorStepSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['minorStepSize'] = (int) $propertyValue; + break; + case 'innerStep': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['innerStep'] = (bool) $propertyValue; + break; + case 'outerStep': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['outerStep'] = (bool) $propertyValue; + break; + case 'outerGrid': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['outerGrid'] = (bool) $propertyValue; + break; + case 'showLabels': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['showLabels'] = (bool) $propertyValue; + break; + case 'labelPadding': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['labelPadding'] = (int) $propertyValue; + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + + /** + * Checks for the cutting point of two lines. + * + * The lines are given by a start position and the direction of the line, + * both as instances of {@link ezcGraphCoordinate}. If no cutting point + * could be calculated, because the lines are parallel the function will + * return false. Otherwise the factor returned can be used to calculate the + * cutting point using the following equatation: + * point = $aStart + $factor * $aDir; + * + * We return the factor instead of the resulting point because it can be + * easily determined from the factor if the cutting point is in "behind" + * the line starting point, or if the distance to the cutting point is + * bigger then the direction vector is long ( $factor > 1 ). + * + * @param ezcGraphCoordinate $aStart + * @param ezcGraphCoordinate $aDir + * @param ezcGraphCoordinate $bStart + * @param ezcGraphCoordinate $bDir + * @return mixed + */ + public function determineLineCuttingPoint( ezcGraphCoordinate $aStart, ezcGraphCoordinate $aDir, ezcGraphCoordinate $bStart, ezcGraphCoordinate $bDir ) + { + // Check if lines are parallel + if ( ( ( abs( $aDir->x ) < .000001 ) && ( abs( $bDir->x ) < .000001 ) ) || + ( ( abs( $aDir->y ) < .000001 ) && ( abs( $bDir->y ) < .000001 ) ) || + ( ( abs( $aDir->x * $bDir->x * $aDir->y * $bDir->y ) > .000001 ) && + ( abs( ( $aDir->x / $aDir->y ) - ( $bDir->x / $bDir->y ) ) < .000001 ) + ) + ) + { + return false; + } + + // Use ? : to prevent division by zero + $denominator = + ( abs( $aDir->y ) > .000001 ? $bDir->y / $aDir->y : .0 ) - + ( abs( $aDir->x ) > .000001 ? $bDir->x / $aDir->x : .0 ); + + // Solve equatation + if ( abs( $denominator ) < .000001 ) + { + return - ( + ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) - + ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) - + ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) + + ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 ) + ); + } + else + { + return - ( + ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) - + ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) - + ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) + + ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 ) + ) / $denominator; + } + } + + /** + * Draw single step on a axis + * + * Draws a step on a axis at the current position + * + * @param ezcGraphRenderer $renderer Renderer to draw the step with + * @param ezcGraphCoordinate $position Position of step + * @param ezcGraphCoordinate $direction Direction of axis + * @param int $axisPosition Position of axis + * @param int $size Step size + * @param ezcGraphColor $color Color of axis + * @return void + */ + public function drawStep( ezcGraphRenderer $renderer, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, $axisPosition, $size, ezcGraphColor $color ) + { + if ( ! ( $this->innerStep || $this->outerStep ) ) + { + return false; + } + + $drawStep = false; + if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) || + ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->outerStep ) || + ( ( $axisPosition === ezcGraph::TOP ) && $this->innerStep ) || + ( ( $axisPosition === ezcGraph::RIGHT ) && $this->outerStep ) || + ( ( $axisPosition === ezcGraph::LEFT ) && $this->innerStep ) ) + { + // Turn direction vector to left by 90 degrees and multiply + // with major step size + $stepStart = new ezcGraphCoordinate( + $position->x + $direction->y * $size, + $position->y - $direction->x * $size + ); + $drawStep = true; + } + else + { + $stepStart = $position; + } + + if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) || + ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->innerStep ) || + ( ( $axisPosition === ezcGraph::TOP ) && $this->outerStep ) || + ( ( $axisPosition === ezcGraph::RIGHT ) && $this->innerStep ) || + ( ( $axisPosition === ezcGraph::LEFT ) && $this->outerStep ) ) + { + // Turn direction vector to right by 90 degrees and multiply + // with major step size + $stepEnd = new ezcGraphCoordinate( + $position->x - $direction->y * $size, + $position->y + $direction->x * $size + ); + $drawStep = true; + } + else + { + $stepEnd = $position; + } + + if ( $drawStep ) + { + $renderer->drawStepLine( + $stepStart, + $stepEnd, + $color + ); + } + } + + /** + * Draw non-rectangular grid lines grid + * + * Draws a grid line at the current position, for non-rectangular axis. + * + * @param ezcGraphRenderer $renderer Renderer to draw the grid with + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $position Position of step + * @param ezcGraphCoordinate $direction Direction of axis + * @param ezcGraphColor $color Color of axis + * @return void + */ + protected function drawNonRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) + { + // Direction of grid line is direction of axis turned right by 90 + // degrees + $gridDirection = new ezcGraphCoordinate( + $direction->y, + - $direction->x + ); + + $cuttingPoints = array(); + foreach ( array( // Bounding lines + array( + 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + 'dir' => new ezcGraphCoordinate( 0, $boundings->y1 - $boundings->y0 ) + ), + array( + 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + 'dir' => new ezcGraphCoordinate( $boundings->x1 - $boundings->x0, 0 ) + ), + array( + 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + 'dir' => new ezcGraphCoordinate( 0, $boundings->y0 - $boundings->y1 ) + ), + array( + 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + 'dir' => new ezcGraphCoordinate( $boundings->x0 - $boundings->x1, 0 ) + ), + ) as $boundingLine ) + { + // Test for cutting points with bounding lines, where cutting + // position is between 0 and 1, which means, that the line is hit + // on the bounding box rectangle. Use these points as a start and + // ending point for the grid lines. There should *always* be + // exactly two points returned. + $cuttingPosition = $this->determineLineCuttingPoint( + $boundingLine['start'], + $boundingLine['dir'], + $position, + $gridDirection + ); + + if ( $cuttingPosition === false ) + { + continue; + } + + $cuttingPosition = abs( $cuttingPosition ); + + if ( ( $cuttingPosition >= 0 ) && + ( $cuttingPosition <= 1 ) ) + { + $cuttingPoints[] = new ezcGraphCoordinate( + $boundingLine['start']->x + $cuttingPosition * $boundingLine['dir']->x, + $boundingLine['start']->y + $cuttingPosition * $boundingLine['dir']->y + ); + } + } + + if ( count( $cuttingPoints ) < 2 ) + { + // This should not happpen + return false; + } + + // Finally draw grid line + $renderer->drawGridLine( + $cuttingPoints[0], + $cuttingPoints[1], + $color + ); + } + + /** + * Draw rectangular grid + * + * Draws a grid line at the current position for rectangular directed axis. + * + * Method special for rectangularly directed axis to minimize the floating + * point calculation inaccuracies. Those are not necessary for rectangles, + * while for non-rectangular directed axis. + * + * @param ezcGraphRenderer $renderer Renderer to draw the grid with + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $position Position of step + * @param ezcGraphCoordinate $direction Direction of axis + * @param ezcGraphColor $color Color of axis + * @return void + */ + protected function drawRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) + { + if ( abs( $direction->x ) < .00001 ) + { + $renderer->drawGridLine( + new ezcGraphCoordinate( + $boundings->x0, + $position->y + ), + new ezcGraphCoordinate( + $boundings->x1, + $position->y + ), + $color + ); + } + else + { + $renderer->drawGridLine( + new ezcGraphCoordinate( + $position->x, + $boundings->y0 + ), + new ezcGraphCoordinate( + $position->x, + $boundings->y1 + ), + $color + ); + } + } + + /** + * Draw grid + * + * Draws a grid line at the current position + * + * @param ezcGraphRenderer $renderer Renderer to draw the grid with + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $position Position of step + * @param ezcGraphCoordinate $direction Direction of axis + * @param ezcGraphColor $color Color of axis + * @return void + */ + protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color ) + { + // Check if the axis direction is rectangular + if ( ( abs( $direction->x ) < .00001 ) || + ( abs( $direction->y ) < .00001 ) ) + { + return $this->drawRectangularGrid( $renderer, $boundings, $position, $direction, $color ); + } + else + { + return $this->drawNonRectangularGrid( $renderer, $boundings, $position, $direction, $color ); + } + } + + /** + * Modify chart boundings + * + * Optionally modify boundings of chart data + * + * @param ezcGraphBoundings $boundings Current boundings of chart + * @param ezcGraphCoordinate $direction Direction of the current axis + * @return ezcGraphBoundings Modified boundings + */ + public function modifyChartBoundings( ezcGraphBoundings $boundings, ezcGraphCoordinate $direction ) + { + return $boundings; + } + + /** + * Modify chart data position + * + * Optionally additionally modify the coodinate of a data point + * + * @param ezcGraphCoordinate $coordinate Data point coordinate + * @return ezcGraphCoordinate Modified coordinate + */ + public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) + { + return $coordinate; + } + + /** + * Get axis space values + * + * Get axis space values, depending on passed parameters. If + * $innerBoundings is given it will be used to caclulat the axis spaces + * available for label rendering. If not given the legacy method will be + * used, which uses the xAxisSpace and yAxisSpace values calcualted by the + * renderer. + * + * Returns an array( $xSpace, $ySpace ), containing the irespective size in + * pixels. Additionally calculates the grid boundings passed by reference. + * + * @param ezcGraphRenderer $renderer + * @param ezcGraphBoundings $boundings + * @param mixed $innerBoundings + * @return array + */ + protected function getAxisSpace( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis, $innerBoundings, &$gridBoundings ) + { + if ( $innerBoundings !== null ) + { + $gridBoundings = clone $innerBoundings; + $xSpace = abs( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 - $boundings->x0 : $boundings->x1 - $innerBoundings->x1 ); + $ySpace = abs( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 - $boundings->y0 : $boundings->y1 - $innerBoundings->y1 ); + } + else + { + $gridBoundings = new ezcGraphBoundings( + $boundings->x0 + ( $xSpace = abs( $renderer->xAxisSpace ) ), + $boundings->y0 + ( $ySpace = abs( $renderer->yAxisSpace ) ), + $boundings->x1 - $xSpace, + $boundings->y1 - $ySpace + ); + } + + if ( $this->outerGrid ) + { + $gridBoundings = $boundings; + } + + return array( $xSpace, $ySpace ); + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + abstract public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis + ); +} + +?> diff --git a/library/ezc/Graph/src/interfaces/chart.php b/library/ezc/Graph/src/interfaces/chart.php index 2c0c51fd95..9c30eb74e4 100644 --- a/library/ezc/Graph/src/interfaces/chart.php +++ b/library/ezc/Graph/src/interfaces/chart.php @@ -1,296 +1,296 @@ -palette = new ezcGraphPaletteTango(); - $this->data = new ezcGraphChartDataContainer( $this ); - - // Add standard elements - $this->addElement( 'background', new ezcGraphChartElementBackground() ); - $this->elements['background']->position = ezcGraph::CENTER | ezcGraph::MIDDLE; - - $this->addElement( 'title', new ezcGraphChartElementText() ); - $this->elements['title']->position = ezcGraph::TOP; - $this->renderElement['title'] = false; - - $this->addElement( 'subtitle', new ezcGraphChartElementText() ); - $this->elements['subtitle']->position = ezcGraph::TOP; - $this->renderElement['subtitle'] = false; - - $this->addElement( 'legend', new ezcGraphChartElementLegend() ); - $this->elements['legend']->position = ezcGraph::LEFT; - - // Define standard renderer and driver - $this->properties['driver'] = new ezcGraphSvgDriver(); - $this->properties['renderer'] = new ezcGraphRenderer2d(); - $this->properties['renderer']->setDriver( $this->driver ); - - // Initialize other properties - $this->properties['renderedFile'] = null; - } - - /** - * Add element to chart - * - * Add a chart element to the chart and perform the required configuration - * tasks for the chart element. - * - * @param string $name Element name - * @param ezcGraphChartElement $element Chart element - * @return void - */ - protected function addElement( $name, ezcGraphChartElement $element ) - { - $this->elements[$name] = $element; - $this->elements[$name]->font = $this->options->font; - $this->elements[$name]->setFromPalette( $this->palette ); - - // Render element by default - $this->renderElement[$name] = true; - } - - /** - * Options write access - * - * @throws ezcBasePropertyNotFoundException - * If Option could not be found - * @throws ezcBaseValueException - * If value is out of range - * @param mixed $propertyName Option name - * @param mixed $propertyValue Option value; - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) { - case 'title': - case 'subtitle': - $this->elements[$propertyName]->title = $propertyValue; - $this->renderElement[$propertyName] = true; - break; - case 'background': - $this->elements[$propertyName]->color = $propertyValue; - break; - case 'legend': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' ); - } - - $this->renderElement['legend'] = (bool) $propertyValue; - break; - case 'renderer': - if ( $propertyValue instanceof ezcGraphRenderer ) - { - $this->properties['renderer'] = $propertyValue; - $this->properties['renderer']->setDriver( $this->driver ); - return $this->properties['renderer']; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRenderer' ); - } - break; - case 'driver': - if ( $propertyValue instanceof ezcGraphDriver ) - { - $this->properties['driver'] = $propertyValue; - $this->properties['renderer']->setDriver( $this->driver ); - return $this->properties['driver']; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphDriver' ); - } - break; - case 'palette': - if ( $propertyValue instanceof ezcGraphPalette ) - { - $this->properties['palette'] = $propertyValue; - $this->setFromPalette( $this->palette ); - } - else - { - throw new ezcBaseValueException( "palette", $propertyValue, "instanceof ezcGraphPalette" ); - } - - break; - case 'renderedFile': - $this->properties['renderedFile'] = (string) $propertyValue; - break; - case 'options': - if ( $propertyValue instanceof ezcGraphChartOptions ) - { - $this->options = $propertyValue; - } - else - { - throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" ); - } - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * Set colors and border fro this element - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - $this->options->font->name = $palette->fontName; - $this->options->font->color = $palette->fontColor; - - foreach ( $this->elements as $element ) - { - $element->setFromPalette( $palette ); - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - if ( array_key_exists( $propertyName, $this->properties ) ) - { - return $this->properties[$propertyName]; - } - - if ( isset( $this->elements[$propertyName] ) ) - { - return $this->elements[$propertyName]; - } - - if ( ( $propertyName === 'options' ) || - ( $propertyName === 'data' ) ) - { - return $this->$propertyName; - } - else - { - throw new ezcGraphNoSuchElementException( $propertyName ); - } - } - - /** - * Returns the default display type of the current chart type. - * - * @return int Display type - */ - abstract public function getDefaultDisplayType(); - - /** - * Return filename of rendered file, and false if no file was yet rendered. - * - * @return mixed - */ - public function getRenderedFile() - { - return ( $this->renderedFile !== null ? $this->renderedFile : false ); - } - - /** - * Renders this chart - * - * Creates basic visual chart elements from the chart to be processed by - * the renderer. - * - * @param int $width - * @param int $height - * @param string $file - * @return void - */ - abstract public function render( $width, $height, $file = null ); - - /** - * Renders this chart to direct output - * - * Does the same as ezcGraphChart::render(), but renders directly to - * output and not into a file. - * - * @param int $width - * @param int $height - * @return void - */ - abstract public function renderToOutput( $width, $height ); -} - -?> +palette = new ezcGraphPaletteTango(); + $this->data = new ezcGraphChartDataContainer( $this ); + + // Add standard elements + $this->addElement( 'background', new ezcGraphChartElementBackground() ); + $this->elements['background']->position = ezcGraph::CENTER | ezcGraph::MIDDLE; + + $this->addElement( 'title', new ezcGraphChartElementText() ); + $this->elements['title']->position = ezcGraph::TOP; + $this->renderElement['title'] = false; + + $this->addElement( 'subtitle', new ezcGraphChartElementText() ); + $this->elements['subtitle']->position = ezcGraph::TOP; + $this->renderElement['subtitle'] = false; + + $this->addElement( 'legend', new ezcGraphChartElementLegend() ); + $this->elements['legend']->position = ezcGraph::LEFT; + + // Define standard renderer and driver + $this->properties['driver'] = new ezcGraphSvgDriver(); + $this->properties['renderer'] = new ezcGraphRenderer2d(); + $this->properties['renderer']->setDriver( $this->driver ); + + // Initialize other properties + $this->properties['renderedFile'] = null; + } + + /** + * Add element to chart + * + * Add a chart element to the chart and perform the required configuration + * tasks for the chart element. + * + * @param string $name Element name + * @param ezcGraphChartElement $element Chart element + * @return void + */ + protected function addElement( $name, ezcGraphChartElement $element ) + { + $this->elements[$name] = $element; + $this->elements[$name]->font = $this->options->font; + $this->elements[$name]->setFromPalette( $this->palette ); + + // Render element by default + $this->renderElement[$name] = true; + } + + /** + * Options write access + * + * @throws ezcBasePropertyNotFoundException + * If Option could not be found + * @throws ezcBaseValueException + * If value is out of range + * @param mixed $propertyName Option name + * @param mixed $propertyValue Option value; + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) { + case 'title': + case 'subtitle': + $this->elements[$propertyName]->title = $propertyValue; + $this->renderElement[$propertyName] = true; + break; + case 'background': + $this->elements[$propertyName]->color = $propertyValue; + break; + case 'legend': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' ); + } + + $this->renderElement['legend'] = (bool) $propertyValue; + break; + case 'renderer': + if ( $propertyValue instanceof ezcGraphRenderer ) + { + $this->properties['renderer'] = $propertyValue; + $this->properties['renderer']->setDriver( $this->driver ); + return $this->properties['renderer']; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRenderer' ); + } + break; + case 'driver': + if ( $propertyValue instanceof ezcGraphDriver ) + { + $this->properties['driver'] = $propertyValue; + $this->properties['renderer']->setDriver( $this->driver ); + return $this->properties['driver']; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphDriver' ); + } + break; + case 'palette': + if ( $propertyValue instanceof ezcGraphPalette ) + { + $this->properties['palette'] = $propertyValue; + $this->setFromPalette( $this->palette ); + } + else + { + throw new ezcBaseValueException( "palette", $propertyValue, "instanceof ezcGraphPalette" ); + } + + break; + case 'renderedFile': + $this->properties['renderedFile'] = (string) $propertyValue; + break; + case 'options': + if ( $propertyValue instanceof ezcGraphChartOptions ) + { + $this->options = $propertyValue; + } + else + { + throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" ); + } + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * Set colors and border fro this element + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + $this->options->font->name = $palette->fontName; + $this->options->font->color = $palette->fontColor; + + foreach ( $this->elements as $element ) + { + $element->setFromPalette( $palette ); + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + if ( array_key_exists( $propertyName, $this->properties ) ) + { + return $this->properties[$propertyName]; + } + + if ( isset( $this->elements[$propertyName] ) ) + { + return $this->elements[$propertyName]; + } + + if ( ( $propertyName === 'options' ) || + ( $propertyName === 'data' ) ) + { + return $this->$propertyName; + } + else + { + throw new ezcGraphNoSuchElementException( $propertyName ); + } + } + + /** + * Returns the default display type of the current chart type. + * + * @return int Display type + */ + abstract public function getDefaultDisplayType(); + + /** + * Return filename of rendered file, and false if no file was yet rendered. + * + * @return mixed + */ + public function getRenderedFile() + { + return ( $this->renderedFile !== null ? $this->renderedFile : false ); + } + + /** + * Renders this chart + * + * Creates basic visual chart elements from the chart to be processed by + * the renderer. + * + * @param int $width + * @param int $height + * @param string $file + * @return void + */ + abstract public function render( $width, $height, $file = null ); + + /** + * Renders this chart to direct output + * + * Does the same as ezcGraphChart::render(), but renders directly to + * output and not into a file. + * + * @param int $width + * @param int $height + * @return void + */ + abstract public function renderToOutput( $width, $height ); +} + +?> diff --git a/library/ezc/Graph/src/interfaces/dataset_property.php b/library/ezc/Graph/src/interfaces/dataset_property.php index 3d48501ff7..72b6035d5f 100644 --- a/library/ezc/Graph/src/interfaces/dataset_property.php +++ b/library/ezc/Graph/src/interfaces/dataset_property.php @@ -1,209 +1,209 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->data['example'] = new ezcGraphArrayDataSet( array( - * 'Foo' => 23, - * 'Bar' => 42, - * ) ); - * - * // Set color for all data points in this data set - * $graph->data['example']->color = '#a40000'; - * - * // Set different color for one special datapoint - * $graph->data['example']->color['Foo'] = '#2e3436'; - * - * $graph->render( 400, 200, 'test.svg' ); - * - * - * @version //autogentag// - * @package Graph - */ -abstract class ezcGraphDataSetProperty implements ArrayAccess -{ - /** - * Default value for this property - * - * @var mixed - */ - protected $defaultValue; - - /** - * Contains specified values for single dataset elements - * - * @var array - */ - protected $dataValue; - - /** - * Contains a reference to the dataset to check for availability of data - * keys - * - * @var ezcGraphDataSet - */ - protected $dataset; - - /** - * Abstract method to contain the check for validity of the value - * - * @param mixed $value - * @return void - */ - abstract protected function checkValue( &$value ); - - /** - * Constructor - * - * @param ezcGraphDataSet $dataset - * @ignore - * @return void - */ - public function __construct( ezcGraphDataSet $dataset ) - { - $this->dataset = $dataset; - } - - /** - * Set the default value for this property - * - * @param string $name Property name - * @param mixed $value Property value - * @return void - */ - public function __set( $name, $value ) - { - if ( $name === 'default' && - $this->checkValue( $value ) ) - { - $this->defaultValue = $value; - } - } - - /** - * Get the default value for this property - * - * @param string $name Property name - * @return mixed - */ - public function __get( $name ) - { - if ( $name === 'default' ) - { - return $this->defaultValue; - } - } - - /** - * Returns if an option exists. - * Allows isset() using ArrayAccess. - * - * @param string $key The name of the option to get. - * @return bool Wether the option exists. - */ - final public function offsetExists( $key ) - { - return isset( $this->dataset[$key] ); - } - - /** - * Returns an option value. - * Get an option value by ArrayAccess. - * - * @param string $key The name of the option to get. - * @return mixed The option value. - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - */ - final public function offsetGet( $key ) - { - if ( isset( $this->dataValue[$key] ) ) - { - return $this->dataValue[$key]; - } - elseif ( isset( $this->dataset[$key] ) ) - { - return $this->defaultValue; - } - else - { - throw new ezcGraphNoSuchDataException( $key ); - } - } - - /** - * Set an option. - * Sets an option using ArrayAccess. - * - * @param string $key The option to set. - * @param mixed $value The value for the option. - * @return void - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @throws ezcBaseValueException - * If a the value for a property is out of range. - */ - public function offsetSet( $key, $value ) - { - if ( isset( $this->dataset[$key] ) && - $this->checkValue( $value ) ) - { - $this->dataValue[$key] = $value; - } - else - { - throw new ezcGraphNoSuchDataException( $key ); - } - } - - /** - * Unset an option. - * Unsets an option using ArrayAccess. - * - * @param string $key The options to unset. - * @return void - * - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @throws ezcBaseValueException - * If a the value for a property is out of range. - */ - final public function offsetUnset( $key ) - { - if ( isset( $this->dataset[$key] ) ) - { - unset( $this->dataValue[$key] ); - } - else - { - throw new ezcGraphNoSuchDataException( $key ); - } - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->data['example'] = new ezcGraphArrayDataSet( array( + * 'Foo' => 23, + * 'Bar' => 42, + * ) ); + * + * // Set color for all data points in this data set + * $graph->data['example']->color = '#a40000'; + * + * // Set different color for one special datapoint + * $graph->data['example']->color['Foo'] = '#2e3436'; + * + * $graph->render( 400, 200, 'test.svg' ); + * + * + * @version 1.4.3 + * @package Graph + */ +abstract class ezcGraphDataSetProperty implements ArrayAccess +{ + /** + * Default value for this property + * + * @var mixed + */ + protected $defaultValue; + + /** + * Contains specified values for single dataset elements + * + * @var array + */ + protected $dataValue; + + /** + * Contains a reference to the dataset to check for availability of data + * keys + * + * @var ezcGraphDataSet + */ + protected $dataset; + + /** + * Abstract method to contain the check for validity of the value + * + * @param mixed $value + * @return void + */ + abstract protected function checkValue( &$value ); + + /** + * Constructor + * + * @param ezcGraphDataSet $dataset + * @ignore + * @return void + */ + public function __construct( ezcGraphDataSet $dataset ) + { + $this->dataset = $dataset; + } + + /** + * Set the default value for this property + * + * @param string $name Property name + * @param mixed $value Property value + * @return void + */ + public function __set( $name, $value ) + { + if ( $name === 'default' && + $this->checkValue( $value ) ) + { + $this->defaultValue = $value; + } + } + + /** + * Get the default value for this property + * + * @param string $name Property name + * @return mixed + */ + public function __get( $name ) + { + if ( $name === 'default' ) + { + return $this->defaultValue; + } + } + + /** + * Returns if an option exists. + * Allows isset() using ArrayAccess. + * + * @param string $key The name of the option to get. + * @return bool Wether the option exists. + */ + final public function offsetExists( $key ) + { + return isset( $this->dataset[$key] ); + } + + /** + * Returns an option value. + * Get an option value by ArrayAccess. + * + * @param string $key The name of the option to get. + * @return mixed The option value. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + */ + final public function offsetGet( $key ) + { + if ( isset( $this->dataValue[$key] ) ) + { + return $this->dataValue[$key]; + } + elseif ( isset( $this->dataset[$key] ) ) + { + return $this->defaultValue; + } + else + { + throw new ezcGraphNoSuchDataException( $key ); + } + } + + /** + * Set an option. + * Sets an option using ArrayAccess. + * + * @param string $key The option to set. + * @param mixed $value The value for the option. + * @return void + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @throws ezcBaseValueException + * If a the value for a property is out of range. + */ + public function offsetSet( $key, $value ) + { + if ( isset( $this->dataset[$key] ) && + $this->checkValue( $value ) ) + { + $this->dataValue[$key] = $value; + } + else + { + throw new ezcGraphNoSuchDataException( $key ); + } + } + + /** + * Unset an option. + * Unsets an option using ArrayAccess. + * + * @param string $key The options to unset. + * @return void + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @throws ezcBaseValueException + * If a the value for a property is out of range. + */ + final public function offsetUnset( $key ) + { + if ( isset( $this->dataset[$key] ) ) + { + unset( $this->dataValue[$key] ); + } + else + { + throw new ezcGraphNoSuchDataException( $key ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/interfaces/driver.php b/library/ezc/Graph/src/interfaces/driver.php index d76793d1de..b4ddc9fb8d 100644 --- a/library/ezc/Graph/src/interfaces/driver.php +++ b/library/ezc/Graph/src/interfaces/driver.php @@ -1,740 +1,740 @@ -options = $propertyValue; - } - else - { - throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" ); - } - break; - - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'options': - return $this->options; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - - /** - * Reduces the size of a polygon - * - * The method takes a polygon defined by a list of points and reduces its - * size by moving all lines to the middle by the given $size value. - * - * The detection of the inner side of the polygon depends on the angle at - * each edge point. This method will always work for 3 edged polygones, - * because the smaller angle will always be on the inner side. For - * polygons with more then 3 edges this method may fail. For ezcGraph this - * is a valid simplification, because we do not have any polygones which - * have an inner angle >= 180 degrees. - * - * @param array(ezcGraphCoordinate) $points - * @param float $size - * @throws ezcGraphReducementFailedException - * @return array( ezcGraphCoordinate ) - */ - protected function reducePolygonSize( array $points, $size ) - { - $pointCount = count( $points ); - - // Build normalized vectors between polygon edge points - $vectors = array(); - $vectorLength = array(); - for ( $i = 0; $i < $pointCount; ++$i ) - { - $nextPoint = ( $i + 1 ) % $pointCount; - $vectors[$i] = ezcGraphVector::fromCoordinate( $points[$nextPoint] ) - ->sub( $points[$i] ); - - // Throw exception if polygon is too small to reduce - $vectorLength[$i] = $vectors[$i]->length(); - if ( $vectorLength[$i] < $size ) - { - throw new ezcGraphReducementFailedException(); - } - $vectors[$i]->unify(); - - // Remove point from list if it the same as the next point - if ( ( $vectors[$i]->x == $vectors[$i]->y ) && ( $vectors[$i]->x == 0 ) ) - { - $pointCount--; - if ( $i === 0 ) - { - $points = array_slice( $points, $i + 1 ); - } - else - { - $points = array_merge( - array_slice( $points, 0, $i ), - array_slice( $points, $i + 1 ) - ); - } - $i--; - } - } - - // Remove vectors and appendant point, if local angle equals zero - // dergrees. - for ( $i = 0; $i < $pointCount; ++$i ) - { - $nextPoint = ( $i + 1 ) % $pointCount; - - if ( ( abs( $vectors[$i]->x - $vectors[$nextPoint]->x ) < .0001 ) && - ( abs( $vectors[$i]->y - $vectors[$nextPoint]->y ) < .0001 ) ) - { - $pointCount--; - - $points = array_merge( - array_slice( $points, 0, $i + 1 ), - array_slice( $points, $i + 2 ) - ); - $vectors = array_merge( - array_slice( $vectors, 0, $i + 1 ), - array_slice( $vectors, $i + 2 ) - ); - $i--; - } - } - - // No reducements for lines - if ( $pointCount <= 2 ) - { - return $points; - } - - // Determine one of the angles - we need to know where the smaller - // angle is, to determine if the inner side of the polygon is on - // the left or right hand. - // - // This is a valid simplification for ezcGraph(, for now). - // - // The sign of the scalar products results indicates on which site - // the smaller angle is, when comparing the orthogonale vector of - // one of the vectors with the other. Why? .. use pen and paper .. - // - // It is sufficant to do this once before iterating over the points, - // because the inner side of the polygon is on the same side of the - // point for each point. - $last = 0; - $next = 1; - - $sign = ( - -$vectors[$last]->y * $vectors[$next]->x + - $vectors[$last]->x * $vectors[$next]->y - ) < 0 ? 1 : -1; - - // Move points to center - $newPoints = array(); - for ( $i = 0; $i < $pointCount; ++$i ) - { - $last = $i; - $next = ( $i + 1 ) % $pointCount; - - // Orthogonal vector with direction based on the side of the inner - // angle - $v = clone $vectors[$next]; - if ( $sign > 0 ) - { - $v->rotateCounterClockwise()->scalar( $size ); - } - else - { - $v->rotateClockwise()->scalar( $size ); - } - - // get last vector not pointing in reverse direction - $lastVector = clone $vectors[$last]; - $lastVector->scalar( -1 ); - - // Calculate new point: Move point to the center site of the - // polygon using the normalized orthogonal vectors next to the - // point and the size as distance to move. - // point + v + size / tan( angle / 2 ) * startVector - $newPoint = clone $vectors[$next]; - $v ->add( - $newPoint - ->scalar( - $size / - tan( - $lastVector->angle( $vectors[$next] ) / 2 - ) - ) - ); - - // A fast guess: If the movement of the point exceeds the length of - // the surrounding edge vectors the angle was to small to perform a - // valid size reducement. In this case we just reduce the length of - // the movement to the minimal length of the surrounding vectors. - // This should fit in most cases. - // - // The correct way to check would be a test, if the calculated - // point is still in the original polygon, but a test for a point - // in a polygon is too expensive. - $movement = $v->length(); - if ( ( $movement > $vectorLength[$last] ) && - ( $movement > $vectorLength[$next] ) ) - { - $v->unify()->scalar( min( $vectorLength[$last], $vectorLength[$next] ) ); - } - - $newPoints[$next] = $v->add( $points[$next] ); - } - - return $newPoints; - } - - /** - * Reduce the size of an ellipse - * - * The method returns a the edgepoints and angles for an ellipse where all - * borders are moved to the inner side of the ellipse by the give $size - * value. - * - * The method returns an - * array ( - * 'center' => (ezcGraphCoordinate) New center point, - * 'start' => (ezcGraphCoordinate) New outer start point, - * 'end' => (ezcGraphCoordinate) New outer end point, - * ) - * - * @param ezcGraphCoordinate $center - * @param float $width - * @param float $height - * @param float $startAngle - * @param float $endAngle - * @param float $size - * @throws ezcGraphReducementFailedException - * @return array - */ - protected function reduceEllipseSize( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, $size ) - { - $oldStartPoint = new ezcGraphVector( - $width * cos( deg2rad( $startAngle ) ) / 2, - $height * sin( deg2rad( $startAngle ) ) / 2 - ); - - $oldEndPoint = new ezcGraphVector( - $width * cos( deg2rad( $endAngle ) ) / 2, - $height * sin( deg2rad( $endAngle ) ) / 2 - ); - - // We always need radian values.. - $degAngle = abs( $endAngle - $startAngle ); - $startAngle = deg2rad( $startAngle ); - $endAngle = deg2rad( $endAngle ); - - // Calculate normalized vectors for the lines spanning the ellipse - $unifiedStartVector = ezcGraphVector::fromCoordinate( $oldStartPoint )->unify(); - $unifiedEndVector = ezcGraphVector::fromCoordinate( $oldEndPoint )->unify(); - $startVector = ezcGraphVector::fromCoordinate( $oldStartPoint ); - $endVector = ezcGraphVector::fromCoordinate( $oldEndPoint ); - - $oldStartPoint->add( $center ); - $oldEndPoint->add( $center ); - - // Use orthogonal vectors of normalized ellipse spanning vectors to - $v = clone $unifiedStartVector; - $v->rotateClockwise()->scalar( $size ); - - // calculate new center point - // center + v + size / tan( angle / 2 ) * startVector - $centerMovement = clone $unifiedStartVector; - $newCenter = $v->add( $centerMovement->scalar( $size / tan( ( $endAngle - $startAngle ) / 2 ) ) )->add( $center ); - - // Test if center is still inside the ellipse, otherwise the sector - // was to small to be reduced - $innerBoundingBoxSize = 0.7 * min( $width, $height ); - if ( ( $newCenter->x < ( $center->x + $innerBoundingBoxSize ) ) && - ( $newCenter->x > ( $center->x - $innerBoundingBoxSize ) ) && - ( $newCenter->y < ( $center->y + $innerBoundingBoxSize ) ) && - ( $newCenter->y > ( $center->y - $innerBoundingBoxSize ) ) ) - { - // Point is in inner bounding box -> everything is OK - } - elseif ( ( $newCenter->x < ( $center->x - $width ) ) || - ( $newCenter->x > ( $center->x + $width ) ) || - ( $newCenter->y < ( $center->y - $height ) ) || - ( $newCenter->y > ( $center->y + $height ) ) ) - { - // Quick outer boundings check - if ( $degAngle > 180 ) - { - // Use old center for very big angles - $newCenter = clone $center; - } - else - { - // Do not draw for very small angles - throw new ezcGraphReducementFailedException(); - } - } - else - { - // Perform exact check - $distance = new ezcGraphVector( - $newCenter->x - $center->x, - $newCenter->y - $center->y - ); - - // Convert elipse to circle for correct angle calculation - $direction = clone $distance; - $direction->y *= ( $width / $height ); - $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); - - $outerPoint = new ezcGraphVector( - sin( $angle ) * $width / 2, - cos( $angle ) * $height / 2 - ); - - // Point is not in ellipse any more - if ( abs( $distance->x ) > abs( $outerPoint->x ) ) - { - if ( $degAngle > 180 ) - { - // Use old center for very big angles - $newCenter = clone $center; - } - else - { - // Do not draw for very small angles - throw new ezcGraphReducementFailedException(); - } - } - } - - // Use start spanning vector and its orthogonal vector to calculate - // new start point - $newStartPoint = clone $oldStartPoint; - - // Create tangent vector from tangent angle - - // Ellipse tangent factor - $ellipseTangentFactor = sqrt( - pow( $height, 2 ) * - pow( cos( $startAngle ), 2 ) + - pow( $width, 2 ) * - pow( sin( $startAngle ), 2 ) - ); - $ellipseTangentVector = new ezcGraphVector( - $width * -sin( $startAngle ) / $ellipseTangentFactor, - $height * cos( $startAngle ) / $ellipseTangentFactor - ); - - // Reverse spanning vector - $innerVector = clone $unifiedStartVector; - $innerVector->scalar( $size )->scalar( -1 ); - - $newStartPoint->add( $innerVector)->add( $ellipseTangentVector->scalar( $size ) ); - $newStartVector = clone $startVector; - $newStartVector->add( $ellipseTangentVector ); - - // Use end spanning vector and its orthogonal vector to calculate - // new end point - $newEndPoint = clone $oldEndPoint; - - // Create tangent vector from tangent angle - - // Ellipse tangent factor - $ellipseTangentFactor = sqrt( - pow( $height, 2 ) * - pow( cos( $endAngle ), 2 ) + - pow( $width, 2 ) * - pow( sin( $endAngle ), 2 ) - ); - $ellipseTangentVector = new ezcGraphVector( - $width * -sin( $endAngle ) / $ellipseTangentFactor, - $height * cos( $endAngle ) / $ellipseTangentFactor - ); - - // Reverse spanning vector - $innerVector = clone $unifiedEndVector; - $innerVector->scalar( $size )->scalar( -1 ); - - $newEndPoint->add( $innerVector )->add( $ellipseTangentVector->scalar( $size )->scalar( -1 ) ); - $newEndVector = clone $endVector; - $newEndVector->add( $ellipseTangentVector ); - - return array( - 'center' => $newCenter, - 'start' => $newStartPoint, - 'end' => $newEndPoint, - 'startAngle' => rad2deg( $startAngle + $startVector->angle( $newStartVector ) ), - 'endAngle' => rad2deg( $endAngle - $endVector->angle( $newEndVector ) ), - ); - } - - /** - * Draws a single polygon. - * - * @param array $points Point array - * @param ezcGraphColor $color Polygon color - * @param mixed $filled Filled - * @param float $thickness Line thickness - * @return void - */ - abstract public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ); - - /** - * Draws a line - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Line color - * @param float $thickness Line thickness - * @return void - */ - abstract public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ); - - /** - * Returns boundings of text depending on the available font extension - * - * @param float $size Textsize - * @param ezcGraphFontOptions $font Font - * @param string $text Text - * @return ezcGraphBoundings Boundings of text - */ - abstract protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ); - - /** - * Test if string fits in a box with given font size - * - * This method splits the text up into tokens and tries to wrap the text - * in an optimal way to fit in the Box defined by width and height. - * - * If the text fits into the box an array with lines is returned, which - * can be used to render the text later: - * array( - * // Lines - * array( 'word', 'word', .. ), - * ) - * Otherwise the function will return false. - * - * @param string $string Text - * @param ezcGraphCoordinate $position Topleft position of the text box - * @param float $width Width of textbox - * @param float $height Height of textbox - * @param int $size Fontsize - * @return mixed Array with lines or false on failure - */ - protected function testFitStringInTextBox( $string, ezcGraphCoordinate $position, $width, $height, $size ) - { - // Tokenize String - $tokens = preg_split( '/\s+/', $string ); - $initialHeight = $height; - - $lines = array( array() ); - $line = 0; - foreach ( $tokens as $nr => $token ) - { - // Add token to tested line - $selectedLine = $lines[$line]; - $selectedLine[] = $token; - - $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $selectedLine ) ); - // Check if line is too long - if ( $boundings->width > $width ) - { - if ( count( $selectedLine ) == 1 ) - { - // Return false if one single word does not fit into one line - // Scale down font size to fit this word in one line - return $width / $boundings->width; - } - else - { - // Put word in next line instead and reduce available height by used space - $lines[++$line][] = $token; - $height -= $size * ( 1 + $this->options->lineSpacing ); - } - } - else - { - // Everything is ok - put token in this line - $lines[$line][] = $token; - } - - // Return false if text exceeds vertical limit - if ( $size > $height ) - { - return 1; - } - } - - // Check width of last line - $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $lines[$line] ) ); - if ( $boundings->width > $width ) - { - return 1; - } - - // It seems to fit - return line array - return $lines; - } - - /** - * If it is allow to shortened the string, this method tries to extract as - * many chars as possible to display a decent amount of characters. - * - * If no complete token (word) does fit, the largest possible amount of - * chars from the first word are taken. If the amount of chars is bigger - * then strlen( shortenedStringPostFix ) * 2 the last chars are replace by - * the postfix. - * - * If one complete word fits the box as many words are taken as possible - * including a appended shortenedStringPostFix. - * - * @param mixed $string - * @param ezcGraphCoordinate $position - * @param mixed $width - * @param mixed $height - * @param mixed $size - * @access protected - * @return void - */ - protected function tryFitShortenedString( $string, ezcGraphCoordinate $position, $width, $height, $size ) - { - $tokens = preg_split( '/\s+/', $string ); - - // Try to fit a complete word first - $boundings = $this->getTextBoundings( - $size, - $this->options->font, - reset( $tokens ) . ( $postfix = $this->options->autoShortenStringPostFix ) - ); - - if ( $boundings->width > $width ) - { - // Not even one word fits the box - $word = reset( $tokens ); - - // Test if first character fits the box - $boundigs = $this->getTextBoundings( - $size, - $this->options->font, - $hit = $word[0] - ); - - if ( $boundigs->width > $width ) - { - // That is a really small box. - throw new ezcGraphFontRenderingException( $string, $size, $width, $height ); - } - - // Try to put more charactes in there - $postLength = strlen( $postfix ); - $wordLength = strlen( $word ); - for ( $i = 2; $i <= $wordLength; ++$i ) - { - $string = substr( $word, 0, $i ); - if ( strlen( $string ) > ( $postLength << 1 ) ) - { - $string = substr( $string, 0, -$postLength ) . $postfix; - } - - $boundigs = $this->getTextBoundings( $size, $this->options->font, $string ); - - if ( $boundigs->width < $width ) - { - $hit = $string; - } - else - { - // Use last string which fit - break; - } - } - } - else - { - // Try to use as many words as possible - $hit = reset( $tokens ); - - for ( $i = 2; $i < count( $tokens ); ++$i ) - { - $string = implode( ' ', array_slice( $tokens, 0, $i ) ) . - $postfix; - - $boundings = $this->getTextBoundings( $size, $this->options->font, $string ); - - if ( $boundings->width <= $width ) - { - $hit .= ' ' . $tokens[$i - 1]; - } - else - { - // Use last valid hit - break; - } - } - - $hit .= $postfix; - } - - return array( array( $hit ) ); - } - - /** - * Writes text in a box of desired size - * - * @param string $string Text - * @param ezcGraphCoordinate $position Top left position - * @param float $width Width of text box - * @param float $height Height of text box - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - abstract public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ); - - /** - * Draws a sector of cirlce - * - * @param ezcGraphCoordinate $center Center of circle - * @param mixed $width Width - * @param mixed $height Height - * @param mixed $startAngle Start angle of circle sector - * @param mixed $endAngle End angle of circle sector - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - abstract public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ); - - /** - * Draws a circular arc - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param integer $width Width of ellipse - * @param integer $height Height of ellipse - * @param integer $size Height of border - * @param float $startAngle Starting angle of circle sector - * @param float $endAngle Ending angle of circle sector - * @param ezcGraphColor $color Color of Border - * @param bool $filled Fill state - * @return void - */ - abstract public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ); - - /** - * Draw circle - * - * @param ezcGraphCoordinate $center Center of ellipse - * @param mixed $width Width of ellipse - * @param mixed $height height of ellipse - * @param ezcGraphColor $color Color - * @param mixed $filled Filled - * @return void - */ - abstract public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ); - - /** - * Draw an image - * - * @param mixed $file Image file - * @param ezcGraphCoordinate $position Top left position - * @param mixed $width Width of image in destination image - * @param mixed $height Height of image in destination image - * @return void - */ - abstract public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ); - - /** - * Return mime type for current image format - * - * @return string - */ - abstract public function getMimeType(); - - /** - * Render image directly to output - * - * The method renders the image directly to the standard output. You - * normally do not want to use this function, because it makes it harder - * to proper cache the generated graphs. - * - * @return void - */ - public function renderToOutput() - { - header( 'Content-Type: ' . $this->getMimeType() ); - $this->render( 'php://output' ); - } - - /** - * Finally save image - * - * @param string $file Destination filename - * @return void - */ - abstract public function render( $file ); -} - -?> +options = $propertyValue; + } + else + { + throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" ); + } + break; + + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'options': + return $this->options; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + + /** + * Reduces the size of a polygon + * + * The method takes a polygon defined by a list of points and reduces its + * size by moving all lines to the middle by the given $size value. + * + * The detection of the inner side of the polygon depends on the angle at + * each edge point. This method will always work for 3 edged polygones, + * because the smaller angle will always be on the inner side. For + * polygons with more then 3 edges this method may fail. For ezcGraph this + * is a valid simplification, because we do not have any polygones which + * have an inner angle >= 180 degrees. + * + * @param array(ezcGraphCoordinate) $points + * @param float $size + * @throws ezcGraphReducementFailedException + * @return array( ezcGraphCoordinate ) + */ + protected function reducePolygonSize( array $points, $size ) + { + $pointCount = count( $points ); + + // Build normalized vectors between polygon edge points + $vectors = array(); + $vectorLength = array(); + for ( $i = 0; $i < $pointCount; ++$i ) + { + $nextPoint = ( $i + 1 ) % $pointCount; + $vectors[$i] = ezcGraphVector::fromCoordinate( $points[$nextPoint] ) + ->sub( $points[$i] ); + + // Throw exception if polygon is too small to reduce + $vectorLength[$i] = $vectors[$i]->length(); + if ( $vectorLength[$i] < $size ) + { + throw new ezcGraphReducementFailedException(); + } + $vectors[$i]->unify(); + + // Remove point from list if it the same as the next point + if ( ( $vectors[$i]->x == $vectors[$i]->y ) && ( $vectors[$i]->x == 0 ) ) + { + $pointCount--; + if ( $i === 0 ) + { + $points = array_slice( $points, $i + 1 ); + } + else + { + $points = array_merge( + array_slice( $points, 0, $i ), + array_slice( $points, $i + 1 ) + ); + } + $i--; + } + } + + // Remove vectors and appendant point, if local angle equals zero + // dergrees. + for ( $i = 0; $i < $pointCount; ++$i ) + { + $nextPoint = ( $i + 1 ) % $pointCount; + + if ( ( abs( $vectors[$i]->x - $vectors[$nextPoint]->x ) < .0001 ) && + ( abs( $vectors[$i]->y - $vectors[$nextPoint]->y ) < .0001 ) ) + { + $pointCount--; + + $points = array_merge( + array_slice( $points, 0, $i + 1 ), + array_slice( $points, $i + 2 ) + ); + $vectors = array_merge( + array_slice( $vectors, 0, $i + 1 ), + array_slice( $vectors, $i + 2 ) + ); + $i--; + } + } + + // No reducements for lines + if ( $pointCount <= 2 ) + { + return $points; + } + + // Determine one of the angles - we need to know where the smaller + // angle is, to determine if the inner side of the polygon is on + // the left or right hand. + // + // This is a valid simplification for ezcGraph(, for now). + // + // The sign of the scalar products results indicates on which site + // the smaller angle is, when comparing the orthogonale vector of + // one of the vectors with the other. Why? .. use pen and paper .. + // + // It is sufficant to do this once before iterating over the points, + // because the inner side of the polygon is on the same side of the + // point for each point. + $last = 0; + $next = 1; + + $sign = ( + -$vectors[$last]->y * $vectors[$next]->x + + $vectors[$last]->x * $vectors[$next]->y + ) < 0 ? 1 : -1; + + // Move points to center + $newPoints = array(); + for ( $i = 0; $i < $pointCount; ++$i ) + { + $last = $i; + $next = ( $i + 1 ) % $pointCount; + + // Orthogonal vector with direction based on the side of the inner + // angle + $v = clone $vectors[$next]; + if ( $sign > 0 ) + { + $v->rotateCounterClockwise()->scalar( $size ); + } + else + { + $v->rotateClockwise()->scalar( $size ); + } + + // get last vector not pointing in reverse direction + $lastVector = clone $vectors[$last]; + $lastVector->scalar( -1 ); + + // Calculate new point: Move point to the center site of the + // polygon using the normalized orthogonal vectors next to the + // point and the size as distance to move. + // point + v + size / tan( angle / 2 ) * startVector + $newPoint = clone $vectors[$next]; + $v ->add( + $newPoint + ->scalar( + $size / + tan( + $lastVector->angle( $vectors[$next] ) / 2 + ) + ) + ); + + // A fast guess: If the movement of the point exceeds the length of + // the surrounding edge vectors the angle was to small to perform a + // valid size reducement. In this case we just reduce the length of + // the movement to the minimal length of the surrounding vectors. + // This should fit in most cases. + // + // The correct way to check would be a test, if the calculated + // point is still in the original polygon, but a test for a point + // in a polygon is too expensive. + $movement = $v->length(); + if ( ( $movement > $vectorLength[$last] ) && + ( $movement > $vectorLength[$next] ) ) + { + $v->unify()->scalar( min( $vectorLength[$last], $vectorLength[$next] ) ); + } + + $newPoints[$next] = $v->add( $points[$next] ); + } + + return $newPoints; + } + + /** + * Reduce the size of an ellipse + * + * The method returns a the edgepoints and angles for an ellipse where all + * borders are moved to the inner side of the ellipse by the give $size + * value. + * + * The method returns an + * array ( + * 'center' => (ezcGraphCoordinate) New center point, + * 'start' => (ezcGraphCoordinate) New outer start point, + * 'end' => (ezcGraphCoordinate) New outer end point, + * ) + * + * @param ezcGraphCoordinate $center + * @param float $width + * @param float $height + * @param float $startAngle + * @param float $endAngle + * @param float $size + * @throws ezcGraphReducementFailedException + * @return array + */ + protected function reduceEllipseSize( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, $size ) + { + $oldStartPoint = new ezcGraphVector( + $width * cos( deg2rad( $startAngle ) ) / 2, + $height * sin( deg2rad( $startAngle ) ) / 2 + ); + + $oldEndPoint = new ezcGraphVector( + $width * cos( deg2rad( $endAngle ) ) / 2, + $height * sin( deg2rad( $endAngle ) ) / 2 + ); + + // We always need radian values.. + $degAngle = abs( $endAngle - $startAngle ); + $startAngle = deg2rad( $startAngle ); + $endAngle = deg2rad( $endAngle ); + + // Calculate normalized vectors for the lines spanning the ellipse + $unifiedStartVector = ezcGraphVector::fromCoordinate( $oldStartPoint )->unify(); + $unifiedEndVector = ezcGraphVector::fromCoordinate( $oldEndPoint )->unify(); + $startVector = ezcGraphVector::fromCoordinate( $oldStartPoint ); + $endVector = ezcGraphVector::fromCoordinate( $oldEndPoint ); + + $oldStartPoint->add( $center ); + $oldEndPoint->add( $center ); + + // Use orthogonal vectors of normalized ellipse spanning vectors to + $v = clone $unifiedStartVector; + $v->rotateClockwise()->scalar( $size ); + + // calculate new center point + // center + v + size / tan( angle / 2 ) * startVector + $centerMovement = clone $unifiedStartVector; + $newCenter = $v->add( $centerMovement->scalar( $size / tan( ( $endAngle - $startAngle ) / 2 ) ) )->add( $center ); + + // Test if center is still inside the ellipse, otherwise the sector + // was to small to be reduced + $innerBoundingBoxSize = 0.7 * min( $width, $height ); + if ( ( $newCenter->x < ( $center->x + $innerBoundingBoxSize ) ) && + ( $newCenter->x > ( $center->x - $innerBoundingBoxSize ) ) && + ( $newCenter->y < ( $center->y + $innerBoundingBoxSize ) ) && + ( $newCenter->y > ( $center->y - $innerBoundingBoxSize ) ) ) + { + // Point is in inner bounding box -> everything is OK + } + elseif ( ( $newCenter->x < ( $center->x - $width ) ) || + ( $newCenter->x > ( $center->x + $width ) ) || + ( $newCenter->y < ( $center->y - $height ) ) || + ( $newCenter->y > ( $center->y + $height ) ) ) + { + // Quick outer boundings check + if ( $degAngle > 180 ) + { + // Use old center for very big angles + $newCenter = clone $center; + } + else + { + // Do not draw for very small angles + throw new ezcGraphReducementFailedException(); + } + } + else + { + // Perform exact check + $distance = new ezcGraphVector( + $newCenter->x - $center->x, + $newCenter->y - $center->y + ); + + // Convert elipse to circle for correct angle calculation + $direction = clone $distance; + $direction->y *= ( $width / $height ); + $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); + + $outerPoint = new ezcGraphVector( + sin( $angle ) * $width / 2, + cos( $angle ) * $height / 2 + ); + + // Point is not in ellipse any more + if ( abs( $distance->x ) > abs( $outerPoint->x ) ) + { + if ( $degAngle > 180 ) + { + // Use old center for very big angles + $newCenter = clone $center; + } + else + { + // Do not draw for very small angles + throw new ezcGraphReducementFailedException(); + } + } + } + + // Use start spanning vector and its orthogonal vector to calculate + // new start point + $newStartPoint = clone $oldStartPoint; + + // Create tangent vector from tangent angle + + // Ellipse tangent factor + $ellipseTangentFactor = sqrt( + pow( $height, 2 ) * + pow( cos( $startAngle ), 2 ) + + pow( $width, 2 ) * + pow( sin( $startAngle ), 2 ) + ); + $ellipseTangentVector = new ezcGraphVector( + $width * -sin( $startAngle ) / $ellipseTangentFactor, + $height * cos( $startAngle ) / $ellipseTangentFactor + ); + + // Reverse spanning vector + $innerVector = clone $unifiedStartVector; + $innerVector->scalar( $size )->scalar( -1 ); + + $newStartPoint->add( $innerVector)->add( $ellipseTangentVector->scalar( $size ) ); + $newStartVector = clone $startVector; + $newStartVector->add( $ellipseTangentVector ); + + // Use end spanning vector and its orthogonal vector to calculate + // new end point + $newEndPoint = clone $oldEndPoint; + + // Create tangent vector from tangent angle + + // Ellipse tangent factor + $ellipseTangentFactor = sqrt( + pow( $height, 2 ) * + pow( cos( $endAngle ), 2 ) + + pow( $width, 2 ) * + pow( sin( $endAngle ), 2 ) + ); + $ellipseTangentVector = new ezcGraphVector( + $width * -sin( $endAngle ) / $ellipseTangentFactor, + $height * cos( $endAngle ) / $ellipseTangentFactor + ); + + // Reverse spanning vector + $innerVector = clone $unifiedEndVector; + $innerVector->scalar( $size )->scalar( -1 ); + + $newEndPoint->add( $innerVector )->add( $ellipseTangentVector->scalar( $size )->scalar( -1 ) ); + $newEndVector = clone $endVector; + $newEndVector->add( $ellipseTangentVector ); + + return array( + 'center' => $newCenter, + 'start' => $newStartPoint, + 'end' => $newEndPoint, + 'startAngle' => rad2deg( $startAngle + $startVector->angle( $newStartVector ) ), + 'endAngle' => rad2deg( $endAngle - $endVector->angle( $newEndVector ) ), + ); + } + + /** + * Draws a single polygon. + * + * @param array $points Point array + * @param ezcGraphColor $color Polygon color + * @param mixed $filled Filled + * @param float $thickness Line thickness + * @return void + */ + abstract public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. ); + + /** + * Draws a line + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Line color + * @param float $thickness Line thickness + * @return void + */ + abstract public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. ); + + /** + * Returns boundings of text depending on the available font extension + * + * @param float $size Textsize + * @param ezcGraphFontOptions $font Font + * @param string $text Text + * @return ezcGraphBoundings Boundings of text + */ + abstract protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text ); + + /** + * Test if string fits in a box with given font size + * + * This method splits the text up into tokens and tries to wrap the text + * in an optimal way to fit in the Box defined by width and height. + * + * If the text fits into the box an array with lines is returned, which + * can be used to render the text later: + * array( + * // Lines + * array( 'word', 'word', .. ), + * ) + * Otherwise the function will return false. + * + * @param string $string Text + * @param ezcGraphCoordinate $position Topleft position of the text box + * @param float $width Width of textbox + * @param float $height Height of textbox + * @param int $size Fontsize + * @return mixed Array with lines or false on failure + */ + protected function testFitStringInTextBox( $string, ezcGraphCoordinate $position, $width, $height, $size ) + { + // Tokenize String + $tokens = preg_split( '/\s+/', $string ); + $initialHeight = $height; + + $lines = array( array() ); + $line = 0; + foreach ( $tokens as $nr => $token ) + { + // Add token to tested line + $selectedLine = $lines[$line]; + $selectedLine[] = $token; + + $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $selectedLine ) ); + // Check if line is too long + if ( $boundings->width > $width ) + { + if ( count( $selectedLine ) == 1 ) + { + // Return false if one single word does not fit into one line + // Scale down font size to fit this word in one line + return $width / $boundings->width; + } + else + { + // Put word in next line instead and reduce available height by used space + $lines[++$line][] = $token; + $height -= $size * ( 1 + $this->options->lineSpacing ); + } + } + else + { + // Everything is ok - put token in this line + $lines[$line][] = $token; + } + + // Return false if text exceeds vertical limit + if ( $size > $height ) + { + return 1; + } + } + + // Check width of last line + $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $lines[$line] ) ); + if ( $boundings->width > $width ) + { + return 1; + } + + // It seems to fit - return line array + return $lines; + } + + /** + * If it is allow to shortened the string, this method tries to extract as + * many chars as possible to display a decent amount of characters. + * + * If no complete token (word) does fit, the largest possible amount of + * chars from the first word are taken. If the amount of chars is bigger + * then strlen( shortenedStringPostFix ) * 2 the last chars are replace by + * the postfix. + * + * If one complete word fits the box as many words are taken as possible + * including a appended shortenedStringPostFix. + * + * @param mixed $string + * @param ezcGraphCoordinate $position + * @param mixed $width + * @param mixed $height + * @param mixed $size + * @access protected + * @return void + */ + protected function tryFitShortenedString( $string, ezcGraphCoordinate $position, $width, $height, $size ) + { + $tokens = preg_split( '/\s+/', $string ); + + // Try to fit a complete word first + $boundings = $this->getTextBoundings( + $size, + $this->options->font, + reset( $tokens ) . ( $postfix = $this->options->autoShortenStringPostFix ) + ); + + if ( $boundings->width > $width ) + { + // Not even one word fits the box + $word = reset( $tokens ); + + // Test if first character fits the box + $boundigs = $this->getTextBoundings( + $size, + $this->options->font, + $hit = $word[0] + ); + + if ( $boundigs->width > $width ) + { + // That is a really small box. + throw new ezcGraphFontRenderingException( $string, $size, $width, $height ); + } + + // Try to put more charactes in there + $postLength = strlen( $postfix ); + $wordLength = strlen( $word ); + for ( $i = 2; $i <= $wordLength; ++$i ) + { + $string = substr( $word, 0, $i ); + if ( strlen( $string ) > ( $postLength << 1 ) ) + { + $string = substr( $string, 0, -$postLength ) . $postfix; + } + + $boundigs = $this->getTextBoundings( $size, $this->options->font, $string ); + + if ( $boundigs->width < $width ) + { + $hit = $string; + } + else + { + // Use last string which fit + break; + } + } + } + else + { + // Try to use as many words as possible + $hit = reset( $tokens ); + + for ( $i = 2; $i < count( $tokens ); ++$i ) + { + $string = implode( ' ', array_slice( $tokens, 0, $i ) ) . + $postfix; + + $boundings = $this->getTextBoundings( $size, $this->options->font, $string ); + + if ( $boundings->width <= $width ) + { + $hit .= ' ' . $tokens[$i - 1]; + } + else + { + // Use last valid hit + break; + } + } + + $hit .= $postfix; + } + + return array( array( $hit ) ); + } + + /** + * Writes text in a box of desired size + * + * @param string $string Text + * @param ezcGraphCoordinate $position Top left position + * @param float $width Width of text box + * @param float $height Height of text box + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + abstract public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null ); + + /** + * Draws a sector of cirlce + * + * @param ezcGraphCoordinate $center Center of circle + * @param mixed $width Width + * @param mixed $height Height + * @param mixed $startAngle Start angle of circle sector + * @param mixed $endAngle End angle of circle sector + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + abstract public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ); + + /** + * Draws a circular arc + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param integer $width Width of ellipse + * @param integer $height Height of ellipse + * @param integer $size Height of border + * @param float $startAngle Starting angle of circle sector + * @param float $endAngle Ending angle of circle sector + * @param ezcGraphColor $color Color of Border + * @param bool $filled Fill state + * @return void + */ + abstract public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ); + + /** + * Draw circle + * + * @param ezcGraphCoordinate $center Center of ellipse + * @param mixed $width Width of ellipse + * @param mixed $height height of ellipse + * @param ezcGraphColor $color Color + * @param mixed $filled Filled + * @return void + */ + abstract public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true ); + + /** + * Draw an image + * + * @param mixed $file Image file + * @param ezcGraphCoordinate $position Top left position + * @param mixed $width Width of image in destination image + * @param mixed $height Height of image in destination image + * @return void + */ + abstract public function drawImage( $file, ezcGraphCoordinate $position, $width, $height ); + + /** + * Return mime type for current image format + * + * @return string + */ + abstract public function getMimeType(); + + /** + * Render image directly to output + * + * The method renders the image directly to the standard output. You + * normally do not want to use this function, because it makes it harder + * to proper cache the generated graphs. + * + * @return void + */ + public function renderToOutput() + { + header( 'Content-Type: ' . $this->getMimeType() ); + $this->render( 'php://output' ); + } + + /** + * Finally save image + * + * @param string $file Destination filename + * @return void + */ + abstract public function render( $file ); +} + +?> diff --git a/library/ezc/Graph/src/interfaces/element.php b/library/ezc/Graph/src/interfaces/element.php index ce3c2f2008..52233a0eeb 100644 --- a/library/ezc/Graph/src/interfaces/element.php +++ b/library/ezc/Graph/src/interfaces/element.php @@ -1,342 +1,342 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->data['example'] = new ezcGraphArrayDataSet( array( - * 'Foo' => 23, - * 'Bar' => 42, - * ) ); - * - * // Set a title and format the title element - * $graph->title = 'Example formatted pie chart'; - * $graph->title->margin = 2; - * $graph->title->background = '#FFFFFF80'; - * $graph->title->border = '#FFFFFF'; - * $graph->title->borderWidth = 1; - * $graph->title->margin = 1; - * $graph->title->padding = 1; - * - * // Format the legend element - * $graph->legend->margin = 2; - * $graph->legend->background = '#FFFFFF80'; - * $graph->legend->border = '#FFFFFF'; - * $graph->legend->borderWidth = 1; - * $graph->legend->margin = 1; - * $graph->legend->padding = 1; - * - * $graph->background->background = '#888a85'; - * - * $graph->render( 400, 250, 'element.svg' ); - * - * - * @property string $title - * Title of chart element. - * @property ezcGraphColor $background - * Background color of chart element. - * @property ezcGraphColor $border - * Border color of chart element. - * @property int $padding - * Distance between border and content of element. - * @property int $margin - * Distance between outer boundings and border of an element. - * @property int $borderWidth - * Border width. - * @property int $position - * Integer defining the elements position in the chart. - * @property int $maxTitleHeight - * Maximum size of the title. - * @property float $portraitTitleSize - * Percentage of boundings which are used for the title with - * position left, right or center. - * @property float $landscapeTitleSize - * Percentage of boundings which are used for the title with - * position top or bottom. - * @property ezcGraphFontOptions $font - * Font used for this element. - * @property-read bool $fontCloned - * Indicates if font configuration was already cloned for this - * specific element. - * @property-read ezcGraphBoundings $boundings - * Boundings of this elements. - * - * @version //autogentag// - * @package Graph - */ -abstract class ezcGraphChartElement extends ezcBaseOptions -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['title'] = false; - $this->properties['background'] = false; - $this->properties['boundings'] = new ezcGraphBoundings(); - $this->properties['border'] = false; - $this->properties['borderWidth'] = 0; - $this->properties['padding'] = 0; - $this->properties['margin'] = 0; - $this->properties['position'] = ezcGraph::LEFT; - $this->properties['maxTitleHeight'] = 16; - $this->properties['portraitTitleSize'] = .15; - $this->properties['landscapeTitleSize'] = .2; - $this->properties['font'] = new ezcGraphFontOptions(); - $this->properties['fontCloned'] = false; - - parent::__construct( $options ); - } - - /** - * Set colors and border fro this element - * - * @param ezcGraphPalette $palette Palette - * @return void - */ - public function setFromPalette( ezcGraphPalette $palette ) - { - $this->properties['border'] = $palette->elementBorderColor; - $this->properties['borderWidth'] = $palette->elementBorderWidth; - $this->properties['background'] = $palette->elementBackground; - $this->properties['padding'] = $palette->padding; - $this->properties['margin'] = $palette->margin; - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'title': - $this->properties['title'] = (string) $propertyValue; - break; - case 'background': - $this->properties['background'] = ezcGraphColor::create( $propertyValue ); - break; - case 'border': - $this->properties['border'] = ezcGraphColor::create( $propertyValue ); - break; - case 'padding': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['padding'] = (int) $propertyValue; - break; - case 'margin': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['margin'] = (int) $propertyValue; - break; - case 'borderWidth': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['borderWidth'] = (int) $propertyValue; - break; - case 'font': - if ( $propertyValue instanceof ezcGraphFontOptions ) - { - $this->properties['font'] = $propertyValue; - } - elseif ( is_string( $propertyValue ) ) - { - if ( !$this->fontCloned ) - { - $this->properties['font'] = clone $this->font; - $this->properties['fontCloned'] = true; - } - - $this->properties['font']->path = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); - } - break; - case 'position': - $positions = array( - ezcGraph::TOP, - ezcGraph::BOTTOM, - ezcGraph::LEFT, - ezcGraph::RIGHT, - ); - - if ( in_array( $propertyValue, $positions, true ) ) - { - $this->properties['position'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( 'position', $propertyValue, 'integer' ); - } - break; - case 'maxTitleHeight': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties['maxTitleHeight'] = (int) $propertyValue; - break; - case 'portraitTitleSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['portraitTitleSize'] = (float) $propertyValue; - break; - case 'landscapeTitleSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['landscapeTitleSize'] = (float) $propertyValue; - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'font': - // Clone font configuration when requested for this element - if ( !$this->fontCloned ) - { - $this->properties['font'] = clone $this->properties['font']; - $this->properties['fontCloned'] = true; - } - return $this->properties['font']; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Renders this chart element - * - * This method receives and returns a part of the canvas where it can be - * rendered on. - * - * @param ezcGraphRenderer $renderer - * @param ezcGraphBoundings $boundings - * @return ezcGraphBoundings Part of canvas, which is still free to draw on - */ - abstract public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ); - - /** - * Returns calculated boundings based on available percentual space of - * given bounding box specified in the elements options and direction of - * the box. - * - * @param ezcGraphBoundings $boundings - * @param int $direction - * @return ezcGraphBoundings - */ - protected function getTitleSize( ezcGraphBoundings $boundings, $direction = ezcGraph::HORIZONTAL ) - { - if ( $direction === ezcGraph::HORIZONTAL ) - { - return min( - $this->maxTitleHeight, - ( $boundings->y1 - $boundings->y0 ) * $this->landscapeTitleSize - ); - } - else - { - return min( - $this->maxTitleHeight, - ( $boundings->y1 - $boundings->y0 ) * $this->portraitTitleSize - ); - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->data['example'] = new ezcGraphArrayDataSet( array( + * 'Foo' => 23, + * 'Bar' => 42, + * ) ); + * + * // Set a title and format the title element + * $graph->title = 'Example formatted pie chart'; + * $graph->title->margin = 2; + * $graph->title->background = '#FFFFFF80'; + * $graph->title->border = '#FFFFFF'; + * $graph->title->borderWidth = 1; + * $graph->title->margin = 1; + * $graph->title->padding = 1; + * + * // Format the legend element + * $graph->legend->margin = 2; + * $graph->legend->background = '#FFFFFF80'; + * $graph->legend->border = '#FFFFFF'; + * $graph->legend->borderWidth = 1; + * $graph->legend->margin = 1; + * $graph->legend->padding = 1; + * + * $graph->background->background = '#888a85'; + * + * $graph->render( 400, 250, 'element.svg' ); + * + * + * @property string $title + * Title of chart element. + * @property ezcGraphColor $background + * Background color of chart element. + * @property ezcGraphColor $border + * Border color of chart element. + * @property int $padding + * Distance between border and content of element. + * @property int $margin + * Distance between outer boundings and border of an element. + * @property int $borderWidth + * Border width. + * @property int $position + * Integer defining the elements position in the chart. + * @property int $maxTitleHeight + * Maximum size of the title. + * @property float $portraitTitleSize + * Percentage of boundings which are used for the title with + * position left, right or center. + * @property float $landscapeTitleSize + * Percentage of boundings which are used for the title with + * position top or bottom. + * @property ezcGraphFontOptions $font + * Font used for this element. + * @property-read bool $fontCloned + * Indicates if font configuration was already cloned for this + * specific element. + * @property-read ezcGraphBoundings $boundings + * Boundings of this elements. + * + * @version 1.4.3 + * @package Graph + */ +abstract class ezcGraphChartElement extends ezcBaseOptions +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['title'] = false; + $this->properties['background'] = false; + $this->properties['boundings'] = new ezcGraphBoundings(); + $this->properties['border'] = false; + $this->properties['borderWidth'] = 0; + $this->properties['padding'] = 0; + $this->properties['margin'] = 0; + $this->properties['position'] = ezcGraph::LEFT; + $this->properties['maxTitleHeight'] = 16; + $this->properties['portraitTitleSize'] = .15; + $this->properties['landscapeTitleSize'] = .2; + $this->properties['font'] = new ezcGraphFontOptions(); + $this->properties['fontCloned'] = false; + + parent::__construct( $options ); + } + + /** + * Set colors and border fro this element + * + * @param ezcGraphPalette $palette Palette + * @return void + */ + public function setFromPalette( ezcGraphPalette $palette ) + { + $this->properties['border'] = $palette->elementBorderColor; + $this->properties['borderWidth'] = $palette->elementBorderWidth; + $this->properties['background'] = $palette->elementBackground; + $this->properties['padding'] = $palette->padding; + $this->properties['margin'] = $palette->margin; + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'title': + $this->properties['title'] = (string) $propertyValue; + break; + case 'background': + $this->properties['background'] = ezcGraphColor::create( $propertyValue ); + break; + case 'border': + $this->properties['border'] = ezcGraphColor::create( $propertyValue ); + break; + case 'padding': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['padding'] = (int) $propertyValue; + break; + case 'margin': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['margin'] = (int) $propertyValue; + break; + case 'borderWidth': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['borderWidth'] = (int) $propertyValue; + break; + case 'font': + if ( $propertyValue instanceof ezcGraphFontOptions ) + { + $this->properties['font'] = $propertyValue; + } + elseif ( is_string( $propertyValue ) ) + { + if ( !$this->fontCloned ) + { + $this->properties['font'] = clone $this->font; + $this->properties['fontCloned'] = true; + } + + $this->properties['font']->path = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); + } + break; + case 'position': + $positions = array( + ezcGraph::TOP, + ezcGraph::BOTTOM, + ezcGraph::LEFT, + ezcGraph::RIGHT, + ); + + if ( in_array( $propertyValue, $positions, true ) ) + { + $this->properties['position'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( 'position', $propertyValue, 'integer' ); + } + break; + case 'maxTitleHeight': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties['maxTitleHeight'] = (int) $propertyValue; + break; + case 'portraitTitleSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['portraitTitleSize'] = (float) $propertyValue; + break; + case 'landscapeTitleSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['landscapeTitleSize'] = (float) $propertyValue; + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'font': + // Clone font configuration when requested for this element + if ( !$this->fontCloned ) + { + $this->properties['font'] = clone $this->properties['font']; + $this->properties['fontCloned'] = true; + } + return $this->properties['font']; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Renders this chart element + * + * This method receives and returns a part of the canvas where it can be + * rendered on. + * + * @param ezcGraphRenderer $renderer + * @param ezcGraphBoundings $boundings + * @return ezcGraphBoundings Part of canvas, which is still free to draw on + */ + abstract public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings ); + + /** + * Returns calculated boundings based on available percentual space of + * given bounding box specified in the elements options and direction of + * the box. + * + * @param ezcGraphBoundings $boundings + * @param int $direction + * @return ezcGraphBoundings + */ + protected function getTitleSize( ezcGraphBoundings $boundings, $direction = ezcGraph::HORIZONTAL ) + { + if ( $direction === ezcGraph::HORIZONTAL ) + { + return min( + $this->maxTitleHeight, + ( $boundings->y1 - $boundings->y0 ) * $this->landscapeTitleSize + ); + } + else + { + return min( + $this->maxTitleHeight, + ( $boundings->y1 - $boundings->y0 ) * $this->portraitTitleSize + ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/interfaces/odometer_renderer.php b/library/ezc/Graph/src/interfaces/odometer_renderer.php index 3255848e54..ed32060a2b 100644 --- a/library/ezc/Graph/src/interfaces/odometer_renderer.php +++ b/library/ezc/Graph/src/interfaces/odometer_renderer.php @@ -1,51 +1,51 @@ - + diff --git a/library/ezc/Graph/src/interfaces/palette.php b/library/ezc/Graph/src/interfaces/palette.php index 8432602bda..fbaa845ce2 100644 --- a/library/ezc/Graph/src/interfaces/palette.php +++ b/library/ezc/Graph/src/interfaces/palette.php @@ -1,284 +1,284 @@ -colorIndex = -1; - $this->symbolIndex = -1; - } - - /** - * Returns the requested property - * - * @param string $propertyName Name of property - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'axisColor': - case 'majorGridColor': - case 'minorGridColor': - case 'fontColor': - case 'chartBackground': - case 'chartBorderColor': - case 'elementBackground': - case 'elementBorderColor': - return ( $this->$propertyName = $this->checkColor( $this->$propertyName ) ); - - case 'dataSetColor': - $this->colorIndex = ( ( $this->colorIndex + 1 ) % count( $this->dataSetColor ) ); - return $this->checkColor( $this->dataSetColor[ $this->colorIndex ] ); - case 'dataSetSymbol': - $this->symbolIndex = ( ( $this->symbolIndex + 1 ) % count( $this->dataSetSymbol ) ); - return $this->dataSetSymbol[ $this->symbolIndex ]; - - case 'fontName': - case 'chartBorderWidth': - case 'elementBorderWidth': - case 'padding': - case 'margin': - return $this->$propertyName; - - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - - /** - * __set - * - * @param mixed $propertyName Property name - * @param mixed $propertyValue Property value - * @access public - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'axisColor': - case 'majorGridColor': - case 'minorGridColor': - case 'fontColor': - case 'chartBackground': - case 'chartBorderColor': - case 'elementBackground': - case 'elementBorderColor': - $this->$propertyName = ezcGraphColor::create( $propertyValue ); - break; - - case 'dataSetColor': - if ( !is_array( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( ezcGraphColor )' ); - } - - $this->dataSetColor = array(); - foreach ( $propertyValue as $value ) - { - $this->dataSetColor[] = ezcGraphColor::create( $value ); - } - $this->colorIndex = -1; - break; - case 'dataSetSymbol': - if ( !is_array( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( (int) ezcGraph::SYMBOL_TYPE )' ); - } - - $this->dataSetSymbol = array(); - foreach ( $propertyValue as $value ) - { - $this->dataSetSymbol[] = (int) $value; - } - $this->symbolIndex = -1; - break; - - case 'fontName': - $this->$propertyName = (string) $propertyValue; - break; - - case 'chartBorderWidth': - case 'elementBorderWidth': - case 'padding': - case 'margin': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->$propertyName = (int) $propertyValue; - break; - - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } -} - -?> +colorIndex = -1; + $this->symbolIndex = -1; + } + + /** + * Returns the requested property + * + * @param string $propertyName Name of property + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'axisColor': + case 'majorGridColor': + case 'minorGridColor': + case 'fontColor': + case 'chartBackground': + case 'chartBorderColor': + case 'elementBackground': + case 'elementBorderColor': + return ( $this->$propertyName = $this->checkColor( $this->$propertyName ) ); + + case 'dataSetColor': + $this->colorIndex = ( ( $this->colorIndex + 1 ) % count( $this->dataSetColor ) ); + return $this->checkColor( $this->dataSetColor[ $this->colorIndex ] ); + case 'dataSetSymbol': + $this->symbolIndex = ( ( $this->symbolIndex + 1 ) % count( $this->dataSetSymbol ) ); + return $this->dataSetSymbol[ $this->symbolIndex ]; + + case 'fontName': + case 'chartBorderWidth': + case 'elementBorderWidth': + case 'padding': + case 'margin': + return $this->$propertyName; + + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + + /** + * __set + * + * @param mixed $propertyName Property name + * @param mixed $propertyValue Property value + * @access public + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'axisColor': + case 'majorGridColor': + case 'minorGridColor': + case 'fontColor': + case 'chartBackground': + case 'chartBorderColor': + case 'elementBackground': + case 'elementBorderColor': + $this->$propertyName = ezcGraphColor::create( $propertyValue ); + break; + + case 'dataSetColor': + if ( !is_array( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( ezcGraphColor )' ); + } + + $this->dataSetColor = array(); + foreach ( $propertyValue as $value ) + { + $this->dataSetColor[] = ezcGraphColor::create( $value ); + } + $this->colorIndex = -1; + break; + case 'dataSetSymbol': + if ( !is_array( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( (int) ezcGraph::SYMBOL_TYPE )' ); + } + + $this->dataSetSymbol = array(); + foreach ( $propertyValue as $value ) + { + $this->dataSetSymbol[] = (int) $value; + } + $this->symbolIndex = -1; + break; + + case 'fontName': + $this->$propertyName = (string) $propertyValue; + break; + + case 'chartBorderWidth': + case 'elementBorderWidth': + case 'padding': + case 'margin': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->$propertyName = (int) $propertyValue; + break; + + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/interfaces/radar_renderer.php b/library/ezc/Graph/src/interfaces/radar_renderer.php index ab511b7c3d..be518162aa 100644 --- a/library/ezc/Graph/src/interfaces/radar_renderer.php +++ b/library/ezc/Graph/src/interfaces/radar_renderer.php @@ -1,54 +1,54 @@ - + diff --git a/library/ezc/Graph/src/interfaces/renderer.php b/library/ezc/Graph/src/interfaces/renderer.php index 535a326b0a..de1418ec35 100644 --- a/library/ezc/Graph/src/interfaces/renderer.php +++ b/library/ezc/Graph/src/interfaces/renderer.php @@ -1,717 +1,717 @@ - - * $chart = new ezcGraphPieChart(); - * $chart->driver = new ezcGraphSvgDriver(); - * - * - * @param ezcGraphDriver $driver Output driver - * @return void - */ - public function setDriver( ezcGraphDriver $driver ) - { - $this->driver = $driver; - } - - /** - * Adds a element reference for context - * - * @param ezcGraphContext $context Dataoint context - * @param mixed $reference Driver dependant reference - * @return void - */ - protected function addElementReference( ezcGraphContext $context, $reference ) - { - $this->elements['data'][$context->dataset][$context->datapoint][] = $reference; - } - - /** - * Return all chart element references - * - * Returns element references for the data sets in the chart, so the - * created graphic may be enhanced later. - * - * The resulting array looks like: - * - * array ( - * legend_url => array ( - * $name => $url | null, - * ... - * ), - * legend => array ( - * $name => $data, - * ... - * ) - * data => array ( - * $dataset => array ( - * $name => $data, - * ... - * ), - * ... - * ) - * ) - * - * - * The legend elements won't show up in the array, if there is no legend - * redered. The URLs are only available, if the url property has been set - * on the respective dataset. - * - * The data assigned to the legends and data elements is completely direver - * dependent. In the SVG and Flash driver there will jsut be some IDs, - * which allow you to reference the affected elements or element groups - * inside the flash or SVG file. - * - * For bitmap formats, like in the Cairo or GD driver, $data will be an - * array of ezcGraphCoordinate objects, which roughly describe the outline - * of the referenced element. For circles and alike the resolution of this - * outline can be configured in the respective driver. - * - * @return array - */ - public function getElementReferences() - { - return $this->elements; - } - - /** - * __get - * - * @param string $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'xAxisSpace': - case 'yAxisSpace': - return $this->$propertyName; - case 'elements': - return $this->elements; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - } - } - - /** - * Draw pie segment - * - * Draws a single pie segment - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of pie segment - * @param float $startAngle Start angle - * @param float $endAngle End angle - * @param mixed $label Label of pie segment - * @param bool $moveOut Move out from middle for hilighting - * @return void - */ - abstract public function drawPieSegment( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - $startAngle = .0, - $endAngle = 360., - $label = false, - $moveOut = false - ); - - /** - * Draw bar - * - * Draws a bar as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $position Position of data point - * @param float $stepSize Space which can be used for bars - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param float $axisPosition Position of axis for drawing filled lines - * @return void - */ - abstract public function drawBar( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $stepSize, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - $axisPosition = 0. - ); - - /** - * Draw data line - * - * Draws a line as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $start Starting point - * @param ezcGraphCoordinate $end Ending point - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor - * @param ezcGraphColor $fillColor Color to fill line with - * @param float $axisPosition Position of axis for drawing filled lines - * @param float $thickness Line thickness - * @return void - */ - abstract public function drawDataLine( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - ezcGraphColor $symbolColor = null, - ezcGraphColor $fillColor = null, - $axisPosition = 0., - $thickness = 1. - ); - - /** - * Draws a highlight textbox for a datapoint. - * - * A highlight textbox for line and bar charts means a box with the current - * value in the graph. - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphCoordinate $end Ending point - * @param float $axisPosition Position of axis for drawing filled lines - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param ezcGraphFontOptions $font Font used for highlight string - * @param string $text Acutual value - * @param int $size Size of highlight text - * @param ezcGraphColor $markLines - * @param int $xOffset - * @param int $yOffset - * @param float $stepSize - * @param int $type - * @return void - */ - abstract public function drawDataHighlightText( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphCoordinate $end, - $axisPosition = 0., - $dataNumber = 1, - $dataCount = 1, - ezcGraphFontOptions $font, - $text, - $size, - ezcGraphColor $markLines = null, - $xOffset = 0, - $yOffset = 0, - $stepSize = 0., - $type = ezcGraph::LINE - ); - - /** - * Draw legend - * - * Will draw a legend in the bounding box - * - * @param ezcGraphBoundings $boundings Bounding of legend - * @param ezcGraphChartElementLegend $legend Legend to draw - * @param int $type Type of legend: Protrait or landscape - * @return void - */ - abstract public function drawLegend( - ezcGraphBoundings $boundings, - ezcGraphChartElementLegend $legend, - $type = ezcGraph::VERTICAL - ); - - /** - * Draw box - * - * Box are wrapping each major chart element and draw border, background - * and title to each chart element. - * - * Optionally a padding and margin for each box can be defined. - * - * @param ezcGraphBoundings $boundings Boundings of the box - * @param ezcGraphColor $background Background color - * @param ezcGraphColor $borderColor Border color - * @param int $borderWidth Border width - * @param int $margin Margin - * @param int $padding Padding - * @param mixed $title Title of the box - * @param int $titleSize Size of title in the box - * @return ezcGraphBoundings Remaining inner boundings - */ - abstract public function drawBox( - ezcGraphBoundings $boundings, - ezcGraphColor $background = null, - ezcGraphColor $borderColor = null, - $borderWidth = 0, - $margin = 0, - $padding = 0, - $title = false, - $titleSize = 16 - ); - - /** - * Draw text - * - * Draws the provided text in the boundings - * - * @param ezcGraphBoundings $boundings Boundings of text - * @param string $text Text - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - abstract public function drawText( - ezcGraphBoundings $boundings, - $text, - $align = ezcGraph::LEFT, - ezcGraphRotation $rotation = null - ); - - /** - * Draw axis - * - * Draws an axis form the provided start point to the end point. A specific - * angle of the axis is not required. - * - * For the labeleing of the axis a sorted array with major steps and an - * array with minor steps is expected, which are build like this: - * array( - * array( - * 'position' => (float), - * 'label' => (string), - * ) - * ) - * where the label is optional. - * - * The label renderer class defines how the labels are rendered. For more - * documentation on this topic have a look at the basic label renderer - * class. - * - * Additionally it can be specified if a major and minor grid are rendered - * by defining a color for them. The axis label is used to add a caption - * for the axis. - * - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $start Start point of axis - * @param ezcGraphCoordinate $end Endpoint of axis - * @param ezcGraphChartElementAxis $axis Axis to render - * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer - * @return void - */ - abstract public function drawAxis( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphAxisLabelRenderer $labelClass = null - ); - - /** - * Draw axis arrow head - * - * Draw an arrow head at the specified position using specified size - * and direction of the error head. Repsects the axisEndStyle option in - * the base renderer options class. - * - * @param ezcGraphCoordinate $position - * @param ezcGraphVector $direction - * @param float $size - * @param ezcGraphColor $color - * @return void - */ - protected function drawAxisArrowHead( ezcGraphCoordinate $position, ezcGraphVector $direction, $size, ezcGraphColor $color ) - { - $orthogonalDirection = clone $direction; - $orthogonalDirection->rotateClockwise(); - - if ( $this->options->axisEndStyle === ezcGraph::ARROW ) - { - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $position->x, - $position->y - ), - new ezcGraphCoordinate( - $position->x - - $orthogonalDirection->x * $size / 2 - + $direction->x * $size, - $position->y - - $orthogonalDirection->y * $size / 2 - + $direction->y * $size - ), - new ezcGraphCoordinate( - $position->x - + $orthogonalDirection->x * $size / 2 - + $direction->x * $size, - $position->y - + $orthogonalDirection->y * $size / 2 - + $direction->y * $size - ), - ), - $color, - true - ); - } - elseif ( $this->options->axisEndStyle !== ezcGraph::NO_SYMBOL ) - { - $topLeft = new ezcGraphCoordinate( - $position->x - + $orthogonalDirection->x * $size / 2 - + $direction->x * $size, - $position->y - + $orthogonalDirection->y * $size / 2 - + $direction->y * $size - ); - - $bottomRight = new ezcGraphCoordinate( - $position->x - - $orthogonalDirection->x * $size / 2, - $position->y - - $orthogonalDirection->y * $size / 2 - ); - - $this->drawSymbol( - $boundings = new ezcGraphBoundings( - min( $topLeft->x, $bottomRight->x ), - min( $topLeft->y, $bottomRight->y ), - max( $topLeft->x, $bottomRight->x ), - max( $topLeft->y, $bottomRight->y ) - ), - $color, - $this->options->axisEndStyle - ); - } - } - - /** - * Draw background image - * - * Draws a background image at the defined position. If repeat is set the - * background image will be repeated like any texture. - * - * @param ezcGraphBoundings $boundings Boundings for the background image - * @param string $file Filename of background image - * @param int $position Position of background image - * @param int $repeat Type of repetition - * @return void - */ - abstract public function drawBackgroundImage( - ezcGraphBoundings $boundings, - $file, - $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE - $repeat = ezcGraph::NO_REPEAT - ); - - /** - * Draw Symbol - * - * Draws a single symbol defined by the symbol constants in ezcGraph. for - * NO_SYMBOL a rect will be drawn. - * - * @param ezcGraphBoundings $boundings Boundings of symbol - * @param ezcGraphColor $color Color of symbol - * @param int $symbol Type of symbol - * @return void - */ - public function drawSymbol( - ezcGraphBoundings $boundings, - ezcGraphColor $color, - $symbol = ezcGraph::NO_SYMBOL ) - { - switch ( $symbol ) - { - case ezcGraph::NO_SYMBOL: - case ezcGraph::SQUARE: - $return = $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $color, - true - ); - - // Draw optional gleam - if ( $this->options->legendSymbolGleam !== false ) - { - $this->driver->drawPolygon( - array( - $topLeft = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - new ezcGraphCoordinate( - $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - $bottomRight = new ezcGraphCoordinate( - $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - ), - new ezcGraphLinearGradient( - $bottomRight, - $topLeft, - $color->darken( -$this->options->legendSymbolGleam ), - $color->darken( $this->options->legendSymbolGleam ) - ), - true - ); - } - return $return; - case ezcGraph::BOX: - $return = $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $color, - false - ); - return $return; - case ezcGraph::DIAMOND: - $return = $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 - ), - new ezcGraphCoordinate( - $boundings->x1, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y1 - ), - new ezcGraphCoordinate( - $boundings->x0, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - ), - $color, - true - ); - - // Draw optional gleam - if ( $this->options->legendSymbolGleam !== false ) - { - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - new ezcGraphCoordinate( - $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - ), - new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.353553391, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.353553391 - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * ( 1 - 0.353553391 ), - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * ( 1 - 0.353553391 ) - ), - $color->darken( -$this->options->legendSymbolGleam ), - $color->darken( $this->options->legendSymbolGleam ) - ), - true - ); - } - return $return; - case ezcGraph::BULLET: - $return = $this->driver->drawCircle( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - $boundings->x1 - $boundings->x0, - $boundings->y1 - $boundings->y0, - $color, - true - ); - - // Draw optional gleam - if ( $this->options->legendSymbolGleam !== false ) - { - $this->driver->drawCircle( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize, - new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.292893219, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.292893219 - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.707106781, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.707106781 - ), - $color->darken( -$this->options->legendSymbolGleam ), - $color->darken( $this->options->legendSymbolGleam ) - ), - true - ); - } - return $return; - case ezcGraph::CIRCLE: - return $this->driver->drawCircle( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ), - $boundings->x1 - $boundings->x0, - $boundings->y1 - $boundings->y0, - $color, - false - ); - } - } - - /** - * Finish rendering - * - * Method is called before the final image is renderer, so that finishing - * operations can be performed here. - * - * @return void - */ - abstract protected function finish(); - - /** - * Reset renderer properties - * - * Reset all renderer properties, which were calculated during the - * rendering process, to offer a clean environment for rerendering. - * - * @return void - */ - protected function resetRenderer() - { - $this->xAxisSpace = false; - $this->yAxisSpace = false; - - // Reset driver, maintaining its configuration - $driverClass = get_class( $this->driver ); - $driverOptions = $this->driver->options; - $this->driver = new $driverClass(); - $this->driver->options = $driverOptions; - } - - /** - * Finally renders the image - * - * @param string $file Filename of destination file - * @return void - */ - public function render( $file = null ) - { - $this->finish(); - - if ( $file === null ) - { - $this->driver->renderToOutput(); - } - else - { - $this->driver->render( $file ); - } - - $this->resetRenderer(); - } -} -?> + + * $chart = new ezcGraphPieChart(); + * $chart->driver = new ezcGraphSvgDriver(); + * + * + * @param ezcGraphDriver $driver Output driver + * @return void + */ + public function setDriver( ezcGraphDriver $driver ) + { + $this->driver = $driver; + } + + /** + * Adds a element reference for context + * + * @param ezcGraphContext $context Dataoint context + * @param mixed $reference Driver dependant reference + * @return void + */ + protected function addElementReference( ezcGraphContext $context, $reference ) + { + $this->elements['data'][$context->dataset][$context->datapoint][] = $reference; + } + + /** + * Return all chart element references + * + * Returns element references for the data sets in the chart, so the + * created graphic may be enhanced later. + * + * The resulting array looks like: + * + * array ( + * legend_url => array ( + * $name => $url | null, + * ... + * ), + * legend => array ( + * $name => $data, + * ... + * ) + * data => array ( + * $dataset => array ( + * $name => $data, + * ... + * ), + * ... + * ) + * ) + * + * + * The legend elements won't show up in the array, if there is no legend + * redered. The URLs are only available, if the url property has been set + * on the respective dataset. + * + * The data assigned to the legends and data elements is completely direver + * dependent. In the SVG and Flash driver there will jsut be some IDs, + * which allow you to reference the affected elements or element groups + * inside the flash or SVG file. + * + * For bitmap formats, like in the Cairo or GD driver, $data will be an + * array of ezcGraphCoordinate objects, which roughly describe the outline + * of the referenced element. For circles and alike the resolution of this + * outline can be configured in the respective driver. + * + * @return array + */ + public function getElementReferences() + { + return $this->elements; + } + + /** + * __get + * + * @param string $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'xAxisSpace': + case 'yAxisSpace': + return $this->$propertyName; + case 'elements': + return $this->elements; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + } + + /** + * Draw pie segment + * + * Draws a single pie segment + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of pie segment + * @param float $startAngle Start angle + * @param float $endAngle End angle + * @param mixed $label Label of pie segment + * @param bool $moveOut Move out from middle for hilighting + * @return void + */ + abstract public function drawPieSegment( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + $startAngle = .0, + $endAngle = 360., + $label = false, + $moveOut = false + ); + + /** + * Draw bar + * + * Draws a bar as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $position Position of data point + * @param float $stepSize Space which can be used for bars + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param float $axisPosition Position of axis for drawing filled lines + * @return void + */ + abstract public function drawBar( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $stepSize, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + $axisPosition = 0. + ); + + /** + * Draw data line + * + * Draws a line as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $start Starting point + * @param ezcGraphCoordinate $end Ending point + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor + * @param ezcGraphColor $fillColor Color to fill line with + * @param float $axisPosition Position of axis for drawing filled lines + * @param float $thickness Line thickness + * @return void + */ + abstract public function drawDataLine( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + ezcGraphColor $symbolColor = null, + ezcGraphColor $fillColor = null, + $axisPosition = 0., + $thickness = 1. + ); + + /** + * Draws a highlight textbox for a datapoint. + * + * A highlight textbox for line and bar charts means a box with the current + * value in the graph. + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphCoordinate $end Ending point + * @param float $axisPosition Position of axis for drawing filled lines + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param ezcGraphFontOptions $font Font used for highlight string + * @param string $text Acutual value + * @param int $size Size of highlight text + * @param ezcGraphColor $markLines + * @param int $xOffset + * @param int $yOffset + * @param float $stepSize + * @param int $type + * @return void + */ + abstract public function drawDataHighlightText( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphCoordinate $end, + $axisPosition = 0., + $dataNumber = 1, + $dataCount = 1, + ezcGraphFontOptions $font, + $text, + $size, + ezcGraphColor $markLines = null, + $xOffset = 0, + $yOffset = 0, + $stepSize = 0., + $type = ezcGraph::LINE + ); + + /** + * Draw legend + * + * Will draw a legend in the bounding box + * + * @param ezcGraphBoundings $boundings Bounding of legend + * @param ezcGraphChartElementLegend $legend Legend to draw + * @param int $type Type of legend: Protrait or landscape + * @return void + */ + abstract public function drawLegend( + ezcGraphBoundings $boundings, + ezcGraphChartElementLegend $legend, + $type = ezcGraph::VERTICAL + ); + + /** + * Draw box + * + * Box are wrapping each major chart element and draw border, background + * and title to each chart element. + * + * Optionally a padding and margin for each box can be defined. + * + * @param ezcGraphBoundings $boundings Boundings of the box + * @param ezcGraphColor $background Background color + * @param ezcGraphColor $borderColor Border color + * @param int $borderWidth Border width + * @param int $margin Margin + * @param int $padding Padding + * @param mixed $title Title of the box + * @param int $titleSize Size of title in the box + * @return ezcGraphBoundings Remaining inner boundings + */ + abstract public function drawBox( + ezcGraphBoundings $boundings, + ezcGraphColor $background = null, + ezcGraphColor $borderColor = null, + $borderWidth = 0, + $margin = 0, + $padding = 0, + $title = false, + $titleSize = 16 + ); + + /** + * Draw text + * + * Draws the provided text in the boundings + * + * @param ezcGraphBoundings $boundings Boundings of text + * @param string $text Text + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + abstract public function drawText( + ezcGraphBoundings $boundings, + $text, + $align = ezcGraph::LEFT, + ezcGraphRotation $rotation = null + ); + + /** + * Draw axis + * + * Draws an axis form the provided start point to the end point. A specific + * angle of the axis is not required. + * + * For the labeleing of the axis a sorted array with major steps and an + * array with minor steps is expected, which are build like this: + * array( + * array( + * 'position' => (float), + * 'label' => (string), + * ) + * ) + * where the label is optional. + * + * The label renderer class defines how the labels are rendered. For more + * documentation on this topic have a look at the basic label renderer + * class. + * + * Additionally it can be specified if a major and minor grid are rendered + * by defining a color for them. The axis label is used to add a caption + * for the axis. + * + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $start Start point of axis + * @param ezcGraphCoordinate $end Endpoint of axis + * @param ezcGraphChartElementAxis $axis Axis to render + * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer + * @return void + */ + abstract public function drawAxis( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphAxisLabelRenderer $labelClass = null + ); + + /** + * Draw axis arrow head + * + * Draw an arrow head at the specified position using specified size + * and direction of the error head. Repsects the axisEndStyle option in + * the base renderer options class. + * + * @param ezcGraphCoordinate $position + * @param ezcGraphVector $direction + * @param float $size + * @param ezcGraphColor $color + * @return void + */ + protected function drawAxisArrowHead( ezcGraphCoordinate $position, ezcGraphVector $direction, $size, ezcGraphColor $color ) + { + $orthogonalDirection = clone $direction; + $orthogonalDirection->rotateClockwise(); + + if ( $this->options->axisEndStyle === ezcGraph::ARROW ) + { + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $position->x, + $position->y + ), + new ezcGraphCoordinate( + $position->x + - $orthogonalDirection->x * $size / 2 + + $direction->x * $size, + $position->y + - $orthogonalDirection->y * $size / 2 + + $direction->y * $size + ), + new ezcGraphCoordinate( + $position->x + + $orthogonalDirection->x * $size / 2 + + $direction->x * $size, + $position->y + + $orthogonalDirection->y * $size / 2 + + $direction->y * $size + ), + ), + $color, + true + ); + } + elseif ( $this->options->axisEndStyle !== ezcGraph::NO_SYMBOL ) + { + $topLeft = new ezcGraphCoordinate( + $position->x + + $orthogonalDirection->x * $size / 2 + + $direction->x * $size, + $position->y + + $orthogonalDirection->y * $size / 2 + + $direction->y * $size + ); + + $bottomRight = new ezcGraphCoordinate( + $position->x + - $orthogonalDirection->x * $size / 2, + $position->y + - $orthogonalDirection->y * $size / 2 + ); + + $this->drawSymbol( + $boundings = new ezcGraphBoundings( + min( $topLeft->x, $bottomRight->x ), + min( $topLeft->y, $bottomRight->y ), + max( $topLeft->x, $bottomRight->x ), + max( $topLeft->y, $bottomRight->y ) + ), + $color, + $this->options->axisEndStyle + ); + } + } + + /** + * Draw background image + * + * Draws a background image at the defined position. If repeat is set the + * background image will be repeated like any texture. + * + * @param ezcGraphBoundings $boundings Boundings for the background image + * @param string $file Filename of background image + * @param int $position Position of background image + * @param int $repeat Type of repetition + * @return void + */ + abstract public function drawBackgroundImage( + ezcGraphBoundings $boundings, + $file, + $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE + $repeat = ezcGraph::NO_REPEAT + ); + + /** + * Draw Symbol + * + * Draws a single symbol defined by the symbol constants in ezcGraph. for + * NO_SYMBOL a rect will be drawn. + * + * @param ezcGraphBoundings $boundings Boundings of symbol + * @param ezcGraphColor $color Color of symbol + * @param int $symbol Type of symbol + * @return void + */ + public function drawSymbol( + ezcGraphBoundings $boundings, + ezcGraphColor $color, + $symbol = ezcGraph::NO_SYMBOL ) + { + switch ( $symbol ) + { + case ezcGraph::NO_SYMBOL: + case ezcGraph::SQUARE: + $return = $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $color, + true + ); + + // Draw optional gleam + if ( $this->options->legendSymbolGleam !== false ) + { + $this->driver->drawPolygon( + array( + $topLeft = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + new ezcGraphCoordinate( + $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + $bottomRight = new ezcGraphCoordinate( + $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + ), + new ezcGraphLinearGradient( + $bottomRight, + $topLeft, + $color->darken( -$this->options->legendSymbolGleam ), + $color->darken( $this->options->legendSymbolGleam ) + ), + true + ); + } + return $return; + case ezcGraph::BOX: + $return = $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $color, + false + ); + return $return; + case ezcGraph::DIAMOND: + $return = $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ), + new ezcGraphCoordinate( + $boundings->x1, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y1 + ), + new ezcGraphCoordinate( + $boundings->x0, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + ), + $color, + true + ); + + // Draw optional gleam + if ( $this->options->legendSymbolGleam !== false ) + { + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + new ezcGraphCoordinate( + $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + ), + new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.353553391, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.353553391 + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * ( 1 - 0.353553391 ), + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * ( 1 - 0.353553391 ) + ), + $color->darken( -$this->options->legendSymbolGleam ), + $color->darken( $this->options->legendSymbolGleam ) + ), + true + ); + } + return $return; + case ezcGraph::BULLET: + $return = $this->driver->drawCircle( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + $boundings->x1 - $boundings->x0, + $boundings->y1 - $boundings->y0, + $color, + true + ); + + // Draw optional gleam + if ( $this->options->legendSymbolGleam !== false ) + { + $this->driver->drawCircle( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize, + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize, + new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.292893219, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.292893219 + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.707106781, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.707106781 + ), + $color->darken( -$this->options->legendSymbolGleam ), + $color->darken( $this->options->legendSymbolGleam ) + ), + true + ); + } + return $return; + case ezcGraph::CIRCLE: + return $this->driver->drawCircle( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ), + $boundings->x1 - $boundings->x0, + $boundings->y1 - $boundings->y0, + $color, + false + ); + } + } + + /** + * Finish rendering + * + * Method is called before the final image is renderer, so that finishing + * operations can be performed here. + * + * @return void + */ + abstract protected function finish(); + + /** + * Reset renderer properties + * + * Reset all renderer properties, which were calculated during the + * rendering process, to offer a clean environment for rerendering. + * + * @return void + */ + protected function resetRenderer() + { + $this->xAxisSpace = false; + $this->yAxisSpace = false; + + // Reset driver, maintaining its configuration + $driverClass = get_class( $this->driver ); + $driverOptions = $this->driver->options; + $this->driver = new $driverClass(); + $this->driver->options = $driverOptions; + } + + /** + * Finally renders the image + * + * @param string $file Filename of destination file + * @return void + */ + public function render( $file = null ) + { + $this->finish(); + + if ( $file === null ) + { + $this->driver->renderToOutput(); + } + else + { + $this->driver->render( $file ); + } + + $this->resetRenderer(); + } +} +?> diff --git a/library/ezc/Graph/src/interfaces/stacked_bar_renderer.php b/library/ezc/Graph/src/interfaces/stacked_bar_renderer.php index 73131513dd..1508bc4305 100644 --- a/library/ezc/Graph/src/interfaces/stacked_bar_renderer.php +++ b/library/ezc/Graph/src/interfaces/stacked_bar_renderer.php @@ -1,46 +1,46 @@ - + diff --git a/library/ezc/Graph/src/math/boundings.php b/library/ezc/Graph/src/math/boundings.php index d026fc9980..948b66de58 100644 --- a/library/ezc/Graph/src/math/boundings.php +++ b/library/ezc/Graph/src/math/boundings.php @@ -1,109 +1,109 @@ -x0 = $x0; - $this->y0 = $y0; - $this->x1 = $x1; - $this->y1 = $y1; - - // Switch values to ensure correct order - if ( $this->x0 > $this->x1 ) - { - $tmp = $this->x0; - $this->x0 = $this->x1; - $this->x1 = $tmp; - } - - if ( $this->y0 > $this->y1 ) - { - $tmp = $this->y0; - $this->y0 = $this->y1; - $this->y1 = $tmp; - } - } - - /** - * Getter for calculated values depending on the boundings. - * - 'width': Width of bounding recangle - * - 'height': Height of bounding recangle - * - * @param string $name Name of property to get - * @return mixed Calculated value - */ - public function __get( $name ) - { - switch ( $name ) - { - case 'width': - return $this->x1 - $this->x0; - case 'height': - return $this->y1 - $this->y0; - default: - throw new ezcBasePropertyNotFoundException( $name ); - } - } -} - -?> +x0 = $x0; + $this->y0 = $y0; + $this->x1 = $x1; + $this->y1 = $y1; + + // Switch values to ensure correct order + if ( $this->x0 > $this->x1 ) + { + $tmp = $this->x0; + $this->x0 = $this->x1; + $this->x1 = $tmp; + } + + if ( $this->y0 > $this->y1 ) + { + $tmp = $this->y0; + $this->y0 = $this->y1; + $this->y1 = $tmp; + } + } + + /** + * Getter for calculated values depending on the boundings. + * - 'width': Width of bounding recangle + * - 'height': Height of bounding recangle + * + * @param string $name Name of property to get + * @return mixed Calculated value + */ + public function __get( $name ) + { + switch ( $name ) + { + case 'width': + return $this->x1 - $this->x0; + case 'height': + return $this->y1 - $this->y0; + default: + throw new ezcBasePropertyNotFoundException( $name ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/math/matrix.php b/library/ezc/Graph/src/math/matrix.php index fea98ced96..24f039b4c0 100644 --- a/library/ezc/Graph/src/math/matrix.php +++ b/library/ezc/Graph/src/math/matrix.php @@ -1,511 +1,511 @@ -rows = max( 1, (int) $rows ); - $this->columns = max( 1, (int) $columns ); - - if ( $values !== null ) - { - $this->fromArray( $values ); - } - else - { - $this->init(); - } - } - - /** - * Create matrix from array - * - * Use an array with float values to set matrix values. - * - * @param array $values Array with values - * @return ezcGraphMatrix Modified matrix - */ - public function fromArray( array $values ) - { - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] = - ( isset( $values[$i][$j] ) - ? (float) $values[$i][$j] - : 0 ); - } - } - - return $this; - } - - /** - * Init matrix - * - * Sets matrix to identity matrix. - * - * @return ezcGraphMatrix Modified matrix - */ - public function init() - { - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] = ( $i === $j ? 1 : 0 ); - } - } - - return $this; - } - - /** - * Returns number of rows - * - * @return int Number of rows - */ - public function rows() - { - return $this->rows; - } - - /** - * Returns number of columns - * - * @return int Number of columns - */ - public function columns() - { - return $this->columns; - } - - /** - * Get a single matrix value - * - * Returns the value of the matrix at the given position - * - * @param int $i Column - * @param int $j Row - * @return float Matrix value - */ - public function get( $i, $j ) - { - if ( ( $i < 0 ) || - ( $i >= $this->rows ) || - ( $j < 0 ) || - ( $j >= $this->columns ) ) - { - throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j ); - } - - return ( !isset( $this->matrix[$i][$j] ) ? .0 : $this->matrix[$i][$j] ); - } - - /** - * Set a single matrix value - * - * Sets the value of the matrix at the given position. - * - * @param int $i Column - * @param int $j Row - * @param float $value Value - * @return ezcGraphMatrix Updated matrix - */ - public function set( $i, $j, $value ) - { - if ( ( $i < 0 ) || - ( $i >= $this->rows ) || - ( $j < 0 ) || - ( $j >= $this->columns ) ) - { - throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j ); - } - - $this->matrix[$i][$j] = $value; - - return $this; - } - - /** - * Adds one matrix to the current one - * - * Calculate the sum of two matrices and returns the resulting matrix. - * - * @param ezcGraphMatrix $matrix Matrix to sum with - * @return ezcGraphMatrix Result matrix - */ - public function add( ezcGraphMatrix $matrix ) - { - if ( ( $this->rows !== $matrix->rows() ) || - ( $this->columns !== $matrix->columns() ) ) - { - throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() ); - } - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] += $matrix->get( $i, $j ); - } - } - - return $this; - } - - /** - * Subtracts matrix from current one - * - * Calculate the diffenrence of two matices and returns the result matrix. - * - * @param ezcGraphMatrix $matrix subtrahend - * @return ezcGraphMatrix Result matrix - */ - public function diff( ezcGraphMatrix $matrix ) - { - if ( ( $this->rows !== $matrix->rows() ) || - ( $this->columns !== $matrix->columns() ) ) - { - throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() ); - } - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] -= $matrix->get( $i, $j ); - } - } - - return $this; - } - - /** - * Scalar multiplication - * - * Multiplies matrix with the given scalar and returns the result matrix - * - * @param float $scalar Scalar - * @return ezcGraphMatrix Result matrix - */ - public function scalar( $scalar ) - { - $scalar = (float) $scalar; - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] *= $scalar; - } - } - } - - /** - * Transpose matrix - * - * @return ezcGraphMatrix Transposed matrix - */ - public function transpose() - { - $matrix = clone $this; - - $this->rows = $matrix->columns(); - $this->columns = $matrix->rows(); - - $this->matrix = array(); - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $this->matrix[$i][$j] = $matrix->get( $j, $i ); - } - } - - return $this; - } - - /** - * Multiplies two matrices - * - * Multiply current matrix with another matrix and returns the result - * matrix. - * - * @param ezcGraphMatrix $matrix Second factor - * @return ezcGraphMatrix Result matrix - */ - public function multiply( ezcGraphMatrix $matrix ) - { - $mColumns = $matrix->columns(); - if ( $this->columns !== ( $mRows = $matrix->rows() ) ) - { - throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $mRows ); - } - - $result = new ezcGraphMatrix( $this->rows, $mColumns ); - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $mColumns; ++$j ) - { - $sum = 0; - for ( $k = 0; $k < $mRows; ++$k ) { - $sum += $this->matrix[$i][$k] * $matrix->get( $k, $j ); - } - - $result->set( $i, $j, $sum ); - } - } - - return $result; - } - - /** - * Solve nonlinear equatation - * - * Tries to solve equatation given by two matrices, with assumption, that: - * A * x = B - * where $this is A, and the paramenter B. x is cosnidered as a vector - * x = ( x^n, x^(n-1), ..., x^2, x, 1 ) - * - * Will return a polynomial solution for x. - * - * See: http://en.wikipedia.org/wiki/Gauss-Newton_algorithm - * - * @param ezcGraphMatrix $matrix B - * @return ezcGraphPolygon Solution of equatation - */ - public function solveNonlinearEquatation( ezcGraphMatrix $matrix ) - { - // Build complete equatation - $equatation = new ezcGraphMatrix( $this->rows, $columns = ( $this->columns + 1 ) ); - - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $this->columns; ++$j ) - { - $equatation->set( $i, $j, $this->matrix[$i][$j] ); - } - $equatation->set( $i, $this->columns, $matrix->get( $i, 0 ) ); - } - - // Compute upper triangular matrix on left side of equatation - for ( $i = 0; $i < ( $this->rows - 1 ); ++$i ) - { - for ( $j = $i + 1; $j < $this->rows; ++$j ) - { - if ( $equatation->get( $j, $i ) !== 0 ) - { - if ( $equatation->get( $j, $i ) == 0 ) - { - continue; - } - else - { - $factor = -( $equatation->get( $i, $i ) / $equatation->get( $j, $i ) ); - } - - for ( $k = $i; $k < $columns; ++$k ) - { - $equatation->set( $j, $k, $equatation->get( $i, $k ) + $factor * $equatation->get( $j, $k ) ); - } - } - } - } - - // Normalize values on left side matrix diagonale - for ( $i = 0; $i < $this->rows; ++$i ) - { - if ( ( ( $value = $equatation->get( $i, $i ) ) != 1 ) && - ( $value != 0 ) ) - { - $factor = 1 / $value; - for ( $k = $i; $k < $columns; ++$k ) - { - $equatation->set( $i, $k, $equatation->get( $i, $k ) * $factor ); - } - } - } - - // Build up solving polynom - $polynom = new ezcGraphPolynom(); - for ( $i = ( $this->rows - 1 ); $i >= 0; --$i ) - { - for ( $j = $i + 1; $j < $this->columns; ++$j ) - { - $equatation->set( - $i, - $this->columns, - $equatation->get( $i, $this->columns ) + ( -$equatation->get( $i, $j ) * $polynom->get( $j ) ) - ); - $equatation->set( $i, $j, 0 ); - } - $polynom->set( $i, $equatation->get( $i, $this->columns ) ); - } - - return $polynom; - } - - /** - * Build LR decomposition from matrix - * - * Use Cholesky-Crout algorithm to get LR decomposition of the current - * matrix. - * - * Will return an array with two matrices: - * array( - * 'l' => (ezcGraphMatrix) $left, - * 'r' => (ezcGraphMatrix) $right, - * ) - * - * @return array( ezcGraphMatrix ) - */ - public function LRdecomposition() - { - /** - * Use Cholesky-Crout algorithm to get LR decomposition - * - * Input: Matrix A ($this) - * - * For i = 1 To n - * For j = i To n - * R(i,j)=A(i,j) - * For k = 1 TO i-1 - * R(i,j)-=L(i,k)*R(k,j) - * end - * end - * For j=i+1 To n - * L(j,i)= A(j,i) - * For k = 1 TO i-1 - * L(j,i)-=L(j,k)*R(k,i) - * end - * L(j,i)/=R(i,i) - * end - * end - * - * Output: matrices L,R - */ - $l = new ezcGraphMatrix( $this->columns, $this->rows ); - $r = new ezcGraphMatrix( $this->columns, $this->rows ); - - for ( $i = 0; $i < $this->columns; ++$i ) - { - for ( $j = $i; $j < $this->rows; ++$j ) - { - $r->set( $i, $j, $this->matrix[$i][$j] ); - for ( $k = 0; $k <= ( $i - 1 ); ++$k ) - { - $r->set( $i, $j, $r->get( $i, $j ) - $l->get( $i, $k ) * $r->get( $k, $j ) ); - } - } - - for ( $j = $i + 1; $j < $this->rows; ++$j ) - { - $l->set( $j, $i, $this->matrix[$j][$i] ); - for ( $k = 0; $k <= ( $i - 1 ); ++$k ) - { - $l->set( $j, $i, $l->get( $j, $i ) - $l->get( $j, $k ) * $r->get( $k, $i ) ); - } - $l->set( $j, $i, $l->get( $j, $i ) / $r->get( $i, $i ) ); - } - } - - return array( - 'l' => $l, - 'r' => $r, - ); - } - - /** - * Returns a string representation of the matrix - * - * @return string - */ - public function __toString() - { - $string = sprintf( "%d x %d matrix:\n", $this->rows, $this->columns ); - - for ( $i = 0; $i < $this->rows; ++$i ) - { - $string .= '| '; - for ( $j = 0; $j < $this->columns; ++$j ) - { - $string .= sprintf( '%04.2f ', $this->get( $i, $j ) ); - } - $string .= "|\n"; - } - - return $string; - } -} -?> +rows = max( 1, (int) $rows ); + $this->columns = max( 1, (int) $columns ); + + if ( $values !== null ) + { + $this->fromArray( $values ); + } + else + { + $this->init(); + } + } + + /** + * Create matrix from array + * + * Use an array with float values to set matrix values. + * + * @param array $values Array with values + * @return ezcGraphMatrix Modified matrix + */ + public function fromArray( array $values ) + { + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] = + ( isset( $values[$i][$j] ) + ? (float) $values[$i][$j] + : 0 ); + } + } + + return $this; + } + + /** + * Init matrix + * + * Sets matrix to identity matrix. + * + * @return ezcGraphMatrix Modified matrix + */ + public function init() + { + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] = ( $i === $j ? 1 : 0 ); + } + } + + return $this; + } + + /** + * Returns number of rows + * + * @return int Number of rows + */ + public function rows() + { + return $this->rows; + } + + /** + * Returns number of columns + * + * @return int Number of columns + */ + public function columns() + { + return $this->columns; + } + + /** + * Get a single matrix value + * + * Returns the value of the matrix at the given position + * + * @param int $i Column + * @param int $j Row + * @return float Matrix value + */ + public function get( $i, $j ) + { + if ( ( $i < 0 ) || + ( $i >= $this->rows ) || + ( $j < 0 ) || + ( $j >= $this->columns ) ) + { + throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j ); + } + + return ( !isset( $this->matrix[$i][$j] ) ? .0 : $this->matrix[$i][$j] ); + } + + /** + * Set a single matrix value + * + * Sets the value of the matrix at the given position. + * + * @param int $i Column + * @param int $j Row + * @param float $value Value + * @return ezcGraphMatrix Updated matrix + */ + public function set( $i, $j, $value ) + { + if ( ( $i < 0 ) || + ( $i >= $this->rows ) || + ( $j < 0 ) || + ( $j >= $this->columns ) ) + { + throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j ); + } + + $this->matrix[$i][$j] = $value; + + return $this; + } + + /** + * Adds one matrix to the current one + * + * Calculate the sum of two matrices and returns the resulting matrix. + * + * @param ezcGraphMatrix $matrix Matrix to sum with + * @return ezcGraphMatrix Result matrix + */ + public function add( ezcGraphMatrix $matrix ) + { + if ( ( $this->rows !== $matrix->rows() ) || + ( $this->columns !== $matrix->columns() ) ) + { + throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() ); + } + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] += $matrix->get( $i, $j ); + } + } + + return $this; + } + + /** + * Subtracts matrix from current one + * + * Calculate the diffenrence of two matices and returns the result matrix. + * + * @param ezcGraphMatrix $matrix subtrahend + * @return ezcGraphMatrix Result matrix + */ + public function diff( ezcGraphMatrix $matrix ) + { + if ( ( $this->rows !== $matrix->rows() ) || + ( $this->columns !== $matrix->columns() ) ) + { + throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() ); + } + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] -= $matrix->get( $i, $j ); + } + } + + return $this; + } + + /** + * Scalar multiplication + * + * Multiplies matrix with the given scalar and returns the result matrix + * + * @param float $scalar Scalar + * @return ezcGraphMatrix Result matrix + */ + public function scalar( $scalar ) + { + $scalar = (float) $scalar; + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] *= $scalar; + } + } + } + + /** + * Transpose matrix + * + * @return ezcGraphMatrix Transposed matrix + */ + public function transpose() + { + $matrix = clone $this; + + $this->rows = $matrix->columns(); + $this->columns = $matrix->rows(); + + $this->matrix = array(); + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $this->matrix[$i][$j] = $matrix->get( $j, $i ); + } + } + + return $this; + } + + /** + * Multiplies two matrices + * + * Multiply current matrix with another matrix and returns the result + * matrix. + * + * @param ezcGraphMatrix $matrix Second factor + * @return ezcGraphMatrix Result matrix + */ + public function multiply( ezcGraphMatrix $matrix ) + { + $mColumns = $matrix->columns(); + if ( $this->columns !== ( $mRows = $matrix->rows() ) ) + { + throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $mRows ); + } + + $result = new ezcGraphMatrix( $this->rows, $mColumns ); + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $mColumns; ++$j ) + { + $sum = 0; + for ( $k = 0; $k < $mRows; ++$k ) { + $sum += $this->matrix[$i][$k] * $matrix->get( $k, $j ); + } + + $result->set( $i, $j, $sum ); + } + } + + return $result; + } + + /** + * Solve nonlinear equatation + * + * Tries to solve equatation given by two matrices, with assumption, that: + * A * x = B + * where $this is A, and the paramenter B. x is cosnidered as a vector + * x = ( x^n, x^(n-1), ..., x^2, x, 1 ) + * + * Will return a polynomial solution for x. + * + * See: http://en.wikipedia.org/wiki/Gauss-Newton_algorithm + * + * @param ezcGraphMatrix $matrix B + * @return ezcGraphPolygon Solution of equatation + */ + public function solveNonlinearEquatation( ezcGraphMatrix $matrix ) + { + // Build complete equatation + $equatation = new ezcGraphMatrix( $this->rows, $columns = ( $this->columns + 1 ) ); + + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $this->columns; ++$j ) + { + $equatation->set( $i, $j, $this->matrix[$i][$j] ); + } + $equatation->set( $i, $this->columns, $matrix->get( $i, 0 ) ); + } + + // Compute upper triangular matrix on left side of equatation + for ( $i = 0; $i < ( $this->rows - 1 ); ++$i ) + { + for ( $j = $i + 1; $j < $this->rows; ++$j ) + { + if ( $equatation->get( $j, $i ) !== 0 ) + { + if ( $equatation->get( $j, $i ) == 0 ) + { + continue; + } + else + { + $factor = -( $equatation->get( $i, $i ) / $equatation->get( $j, $i ) ); + } + + for ( $k = $i; $k < $columns; ++$k ) + { + $equatation->set( $j, $k, $equatation->get( $i, $k ) + $factor * $equatation->get( $j, $k ) ); + } + } + } + } + + // Normalize values on left side matrix diagonale + for ( $i = 0; $i < $this->rows; ++$i ) + { + if ( ( ( $value = $equatation->get( $i, $i ) ) != 1 ) && + ( $value != 0 ) ) + { + $factor = 1 / $value; + for ( $k = $i; $k < $columns; ++$k ) + { + $equatation->set( $i, $k, $equatation->get( $i, $k ) * $factor ); + } + } + } + + // Build up solving polynom + $polynom = new ezcGraphPolynom(); + for ( $i = ( $this->rows - 1 ); $i >= 0; --$i ) + { + for ( $j = $i + 1; $j < $this->columns; ++$j ) + { + $equatation->set( + $i, + $this->columns, + $equatation->get( $i, $this->columns ) + ( -$equatation->get( $i, $j ) * $polynom->get( $j ) ) + ); + $equatation->set( $i, $j, 0 ); + } + $polynom->set( $i, $equatation->get( $i, $this->columns ) ); + } + + return $polynom; + } + + /** + * Build LR decomposition from matrix + * + * Use Cholesky-Crout algorithm to get LR decomposition of the current + * matrix. + * + * Will return an array with two matrices: + * array( + * 'l' => (ezcGraphMatrix) $left, + * 'r' => (ezcGraphMatrix) $right, + * ) + * + * @return array( ezcGraphMatrix ) + */ + public function LRdecomposition() + { + /** + * Use Cholesky-Crout algorithm to get LR decomposition + * + * Input: Matrix A ($this) + * + * For i = 1 To n + * For j = i To n + * R(i,j)=A(i,j) + * For k = 1 TO i-1 + * R(i,j)-=L(i,k)*R(k,j) + * end + * end + * For j=i+1 To n + * L(j,i)= A(j,i) + * For k = 1 TO i-1 + * L(j,i)-=L(j,k)*R(k,i) + * end + * L(j,i)/=R(i,i) + * end + * end + * + * Output: matrices L,R + */ + $l = new ezcGraphMatrix( $this->columns, $this->rows ); + $r = new ezcGraphMatrix( $this->columns, $this->rows ); + + for ( $i = 0; $i < $this->columns; ++$i ) + { + for ( $j = $i; $j < $this->rows; ++$j ) + { + $r->set( $i, $j, $this->matrix[$i][$j] ); + for ( $k = 0; $k <= ( $i - 1 ); ++$k ) + { + $r->set( $i, $j, $r->get( $i, $j ) - $l->get( $i, $k ) * $r->get( $k, $j ) ); + } + } + + for ( $j = $i + 1; $j < $this->rows; ++$j ) + { + $l->set( $j, $i, $this->matrix[$j][$i] ); + for ( $k = 0; $k <= ( $i - 1 ); ++$k ) + { + $l->set( $j, $i, $l->get( $j, $i ) - $l->get( $j, $k ) * $r->get( $k, $i ) ); + } + $l->set( $j, $i, $l->get( $j, $i ) / $r->get( $i, $i ) ); + } + } + + return array( + 'l' => $l, + 'r' => $r, + ); + } + + /** + * Returns a string representation of the matrix + * + * @return string + */ + public function __toString() + { + $string = sprintf( "%d x %d matrix:\n", $this->rows, $this->columns ); + + for ( $i = 0; $i < $this->rows; ++$i ) + { + $string .= '| '; + for ( $j = 0; $j < $this->columns; ++$j ) + { + $string .= sprintf( '%04.2f ', $this->get( $i, $j ) ); + } + $string .= "|\n"; + } + + return $string; + } +} +?> diff --git a/library/ezc/Graph/src/math/polynom.php b/library/ezc/Graph/src/math/polynom.php index a09021749b..70ad98c577 100644 --- a/library/ezc/Graph/src/math/polynom.php +++ b/library/ezc/Graph/src/math/polynom.php @@ -1,259 +1,259 @@ - - * // Equivalent to: x^2 + .5 - * $polynom = new ezcGraphPolynom( array( 2 => 1, 0 => .5 ) ); - * - * // Calculate result for x = 1, echos: 1.5 - * echo $polynom->evaluate( 1 ), PHP_EOL; - * - * // Build the sum with another polynom - * $polynom->add( new ezcGraphPolynom( array( 1 => 1 ) ) ); - * - * // Print polynom, echos: - * // x^2 + x + 5.00e-1 - * echo $polynom, PHP_EOL; - * - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphPolynom -{ - /** - * Factors of the polynom - * - * An example: - * Polynom: - * 2 * x^3 + .5 * x - 3 - * Array: - * array ( - * (int) 3 => (float) 2, - * (int) 1 => (float) .5, - * (int) 0 => (float) -3, - * ) - * - * @var array - */ - protected $values; - - // @TODO: Introduce precision option for string output? - - /** - * Constructor - * - * Constructs a polynom object from given array, where the key is the - * exponent and the value the factor. - * An example: - * Polynom: - * 2 * x^3 + .5 * x - 3 - * Array: - * array ( - * (int) 3 => (float) 2, - * (int) 1 => (float) .5, - * (int) 0 => (float) -3, - * ) - * - * @param array $values Array with values - * @return ezcGraphPolynom - */ - public function __construct( array $values = array() ) - { - foreach ( $values as $exponent => $factor ) - { - $this->values[(int) $exponent] = (float) $factor; - } - } - - /** - * Initialise a polygon - * - * Initialise a polygon of the given order. Sets all factors to 0. - * - * @param int $order Order of polygon - * @return ezcGraphPolynom Created polynom - */ - public function init( $order ) - { - for ( $i = 0; $i <= $order; ++$i ) - { - $this->values[$i] = 0; - } - - return $this; - } - - /** - * Return factor for one exponent - * - * @param int $exponent Exponent - * @return float Factor - */ - public function get( $exponent ) - { - if ( !isset( $this->values[$exponent] ) ) - { - return 0; - } - else - { - return $this->values[$exponent]; - } - } - - /** - * Set the factor for one exponent - * - * @param int $exponent Exponent - * @param float $factor Factor - * @return ezcGraphPolynom Modified polynom - */ - public function set( $exponent, $factor ) - { - $this->values[(int) $exponent] = (float) $factor; - - return $this; - } - - /** - * Returns the order of the polynom - * - * @return int Polynom order - */ - public function getOrder() - { - return max( array_keys( $this->values ) ); - } - - /** - * Adds polynom to current polynom - * - * @param ezcGraphPolynom $polynom Polynom to add - * @return ezcGraphPolynom Modified polynom - */ - public function add( ezcGraphPolynom $polynom ) - { - $order = max( - $this->getOrder(), - $polynom->getOrder() - ); - - for ( $i = 0; $i <= $order; ++$i ) - { - $this->set( $i, $this->get( $i ) + $polynom->get( $i ) ); - } - - return $this; - } - - /** - * Evaluate Polynom with a given value - * - * @param float $x Value - * @return float Result - */ - public function evaluate( $x ) - { - $value = 0; - foreach ( $this->values as $exponent => $factor ) - { - $value += $factor * pow( $x, $exponent ); - } - - return $value; - } - - /** - * Returns a string represenation of the polynom - * - * @return string String representation of polynom - */ - public function __toString() - { - krsort( $this->values ); - $string = ''; - - foreach ( $this->values as $exponent => $factor ) - { - if ( $factor == 0 ) - { - continue; - } - - $string .= ( $factor < 0 ? ' - ' : ' + ' ); - - $factor = abs( $factor ); - switch ( true ) - { - case abs( 1 - $factor ) < .0001: - // No not append, if factor is ~1 - break; - case $factor < 1: - case $factor >= 1000: - $string .= sprintf( '%.2e ', $factor ); - break; - case $factor >= 100: - $string .= sprintf( '%.0f ', $factor ); - break; - case $factor >= 10: - $string .= sprintf( '%.1f ', $factor ); - break; - default: - $string .= sprintf( '%.2f ', $factor ); - break; - } - - switch ( true ) - { - case $exponent > 1: - $string .= sprintf( 'x^%d', $exponent ); - break; - case $exponent === 1: - $string .= 'x'; - break; - case $exponent === 0: - if ( abs( 1 - $factor ) < .0001 ) - { - $string .= '1'; - } - break; - } - } - - if ( substr( $string, 0, 3 ) === ' + ' ) - { - $string = substr( $string, 3 ); - } - else - { - $string = '-' . substr( $string, 3 ); - } - - return trim( $string ); - } -} -?> + + * // Equivalent to: x^2 + .5 + * $polynom = new ezcGraphPolynom( array( 2 => 1, 0 => .5 ) ); + * + * // Calculate result for x = 1, echos: 1.5 + * echo $polynom->evaluate( 1 ), PHP_EOL; + * + * // Build the sum with another polynom + * $polynom->add( new ezcGraphPolynom( array( 1 => 1 ) ) ); + * + * // Print polynom, echos: + * // x^2 + x + 5.00e-1 + * echo $polynom, PHP_EOL; + * + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphPolynom +{ + /** + * Factors of the polynom + * + * An example: + * Polynom: + * 2 * x^3 + .5 * x - 3 + * Array: + * array ( + * (int) 3 => (float) 2, + * (int) 1 => (float) .5, + * (int) 0 => (float) -3, + * ) + * + * @var array + */ + protected $values; + + // @TODO: Introduce precision option for string output? + + /** + * Constructor + * + * Constructs a polynom object from given array, where the key is the + * exponent and the value the factor. + * An example: + * Polynom: + * 2 * x^3 + .5 * x - 3 + * Array: + * array ( + * (int) 3 => (float) 2, + * (int) 1 => (float) .5, + * (int) 0 => (float) -3, + * ) + * + * @param array $values Array with values + * @return ezcGraphPolynom + */ + public function __construct( array $values = array() ) + { + foreach ( $values as $exponent => $factor ) + { + $this->values[(int) $exponent] = (float) $factor; + } + } + + /** + * Initialise a polygon + * + * Initialise a polygon of the given order. Sets all factors to 0. + * + * @param int $order Order of polygon + * @return ezcGraphPolynom Created polynom + */ + public function init( $order ) + { + for ( $i = 0; $i <= $order; ++$i ) + { + $this->values[$i] = 0; + } + + return $this; + } + + /** + * Return factor for one exponent + * + * @param int $exponent Exponent + * @return float Factor + */ + public function get( $exponent ) + { + if ( !isset( $this->values[$exponent] ) ) + { + return 0; + } + else + { + return $this->values[$exponent]; + } + } + + /** + * Set the factor for one exponent + * + * @param int $exponent Exponent + * @param float $factor Factor + * @return ezcGraphPolynom Modified polynom + */ + public function set( $exponent, $factor ) + { + $this->values[(int) $exponent] = (float) $factor; + + return $this; + } + + /** + * Returns the order of the polynom + * + * @return int Polynom order + */ + public function getOrder() + { + return max( array_keys( $this->values ) ); + } + + /** + * Adds polynom to current polynom + * + * @param ezcGraphPolynom $polynom Polynom to add + * @return ezcGraphPolynom Modified polynom + */ + public function add( ezcGraphPolynom $polynom ) + { + $order = max( + $this->getOrder(), + $polynom->getOrder() + ); + + for ( $i = 0; $i <= $order; ++$i ) + { + $this->set( $i, $this->get( $i ) + $polynom->get( $i ) ); + } + + return $this; + } + + /** + * Evaluate Polynom with a given value + * + * @param float $x Value + * @return float Result + */ + public function evaluate( $x ) + { + $value = 0; + foreach ( $this->values as $exponent => $factor ) + { + $value += $factor * pow( $x, $exponent ); + } + + return $value; + } + + /** + * Returns a string represenation of the polynom + * + * @return string String representation of polynom + */ + public function __toString() + { + krsort( $this->values ); + $string = ''; + + foreach ( $this->values as $exponent => $factor ) + { + if ( $factor == 0 ) + { + continue; + } + + $string .= ( $factor < 0 ? ' - ' : ' + ' ); + + $factor = abs( $factor ); + switch ( true ) + { + case abs( 1 - $factor ) < .0001: + // No not append, if factor is ~1 + break; + case $factor < 1: + case $factor >= 1000: + $string .= sprintf( '%.2e ', $factor ); + break; + case $factor >= 100: + $string .= sprintf( '%.0f ', $factor ); + break; + case $factor >= 10: + $string .= sprintf( '%.1f ', $factor ); + break; + default: + $string .= sprintf( '%.2f ', $factor ); + break; + } + + switch ( true ) + { + case $exponent > 1: + $string .= sprintf( 'x^%d', $exponent ); + break; + case $exponent === 1: + $string .= 'x'; + break; + case $exponent === 0: + if ( abs( 1 - $factor ) < .0001 ) + { + $string .= '1'; + } + break; + } + } + + if ( substr( $string, 0, 3 ) === ' + ' ) + { + $string = substr( $string, 3 ); + } + else + { + $string = '-' . substr( $string, 3 ); + } + + return trim( $string ); + } +} +?> diff --git a/library/ezc/Graph/src/math/rotation.php b/library/ezc/Graph/src/math/rotation.php index 1124d89856..0bee1bdf36 100644 --- a/library/ezc/Graph/src/math/rotation.php +++ b/library/ezc/Graph/src/math/rotation.php @@ -1,99 +1,99 @@ -rotation = (float) $rotation; - - if ( $center === null ) - { - $this->center = new ezcGraphCoordinate( 0, 0 ); - - $clockwiseRotation = deg2rad( $rotation ); - $rotationMatrixArray = array( - array( cos( $clockwiseRotation ), -sin( $clockwiseRotation ), 0 ), - array( sin( $clockwiseRotation ), cos( $clockwiseRotation ), 0 ), - array( 0, 0, 1 ), - ); - - return parent::__construct( $rotationMatrixArray ); - } - - parent::__construct(); - - $this->center = $center; - - $this->multiply( new ezcGraphTranslation( $center->x, $center->y ) ); - $this->multiply( new ezcGraphRotation( $rotation ) ); - $this->multiply( new ezcGraphTranslation( -$center->x, -$center->y ) ); - } - - /** - * Return rotaion angle in degrees - * - * @return float - */ - public function getRotation() - { - return $this->rotation; - } - - /** - * Return the center point of the current rotation - * - * @return ezcGraphCoordinate - */ - public function getCenter() - { - return $this->center; - } -} - -?> +rotation = (float) $rotation; + + if ( $center === null ) + { + $this->center = new ezcGraphCoordinate( 0, 0 ); + + $clockwiseRotation = deg2rad( $rotation ); + $rotationMatrixArray = array( + array( cos( $clockwiseRotation ), -sin( $clockwiseRotation ), 0 ), + array( sin( $clockwiseRotation ), cos( $clockwiseRotation ), 0 ), + array( 0, 0, 1 ), + ); + + return parent::__construct( $rotationMatrixArray ); + } + + parent::__construct(); + + $this->center = $center; + + $this->multiply( new ezcGraphTranslation( $center->x, $center->y ) ); + $this->multiply( new ezcGraphRotation( $rotation ) ); + $this->multiply( new ezcGraphTranslation( -$center->x, -$center->y ) ); + } + + /** + * Return rotaion angle in degrees + * + * @return float + */ + public function getRotation() + { + return $this->rotation; + } + + /** + * Return the center point of the current rotation + * + * @return ezcGraphCoordinate + */ + public function getCenter() + { + return $this->center; + } +} + +?> diff --git a/library/ezc/Graph/src/math/transformation.php b/library/ezc/Graph/src/math/transformation.php index 7725e06970..7ff7c8aa8f 100644 --- a/library/ezc/Graph/src/math/transformation.php +++ b/library/ezc/Graph/src/math/transformation.php @@ -1,96 +1,96 @@ -columns(); - - // We want to ensure, that the matrix stays 3x3 - if ( ( $this->columns !== $matrix->rows() ) && - ( $this->rows !== $mColumns ) ) - { - throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $matrix->rows() ); - } - - $result = parent::multiply( $matrix ); - - // The matrix dimensions stay the same, so that we can modify $this. - for ( $i = 0; $i < $this->rows; ++$i ) - { - for ( $j = 0; $j < $mColumns; ++$j ) - { - $this->set( $i, $j, $result->get( $i, $j ) ); - } - } - - return $this; - } - - /** - * Transform a coordinate with the current transformation matrix. - * - * @param ezcGraphCoordinate $coordinate - * @return ezcGraphCoordinate - */ - public function transformCoordinate( ezcGraphCoordinate $coordinate ) - { - $vector = new ezcGraphMatrix( 3, 1, array( array( $coordinate->x ), array( $coordinate->y ), array( 1 ) ) ); - $vector = parent::multiply( $vector ); - - return new ezcGraphCoordinate( $vector->get( 0, 0 ), $vector->get( 1, 0 ) ); - } -} - -?> +columns(); + + // We want to ensure, that the matrix stays 3x3 + if ( ( $this->columns !== $matrix->rows() ) && + ( $this->rows !== $mColumns ) ) + { + throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $matrix->rows() ); + } + + $result = parent::multiply( $matrix ); + + // The matrix dimensions stay the same, so that we can modify $this. + for ( $i = 0; $i < $this->rows; ++$i ) + { + for ( $j = 0; $j < $mColumns; ++$j ) + { + $this->set( $i, $j, $result->get( $i, $j ) ); + } + } + + return $this; + } + + /** + * Transform a coordinate with the current transformation matrix. + * + * @param ezcGraphCoordinate $coordinate + * @return ezcGraphCoordinate + */ + public function transformCoordinate( ezcGraphCoordinate $coordinate ) + { + $vector = new ezcGraphMatrix( 3, 1, array( array( $coordinate->x ), array( $coordinate->y ), array( 1 ) ) ); + $vector = parent::multiply( $vector ); + + return new ezcGraphCoordinate( $vector->get( 0, 0 ), $vector->get( 1, 0 ) ); + } +} + +?> diff --git a/library/ezc/Graph/src/math/translation.php b/library/ezc/Graph/src/math/translation.php index d0046e6d74..b4f4874b52 100644 --- a/library/ezc/Graph/src/math/translation.php +++ b/library/ezc/Graph/src/math/translation.php @@ -1,47 +1,47 @@ - + diff --git a/library/ezc/Graph/src/math/vector.php b/library/ezc/Graph/src/math/vector.php index 5962fc18f2..2f45591d38 100644 --- a/library/ezc/Graph/src/math/vector.php +++ b/library/ezc/Graph/src/math/vector.php @@ -1,194 +1,194 @@ -x; - $this->x = $this->y; - $this->y = -$tmp; - - return $this; - } - - /** - * Rotates vector to the right by 90 degrees - * - * @return void - */ - public function rotateClockwise() - { - $tmp = $this->x; - $this->x = -$this->y; - $this->y = $tmp; - - return $this; - } - - /** - * Unifies vector length to 1 - * - * @return void - */ - public function unify() - { - $length = $this->length(); - if ( $length == 0 ) - { - return $this; - } - - $this->x /= $length; - $this->y /= $length; - - return $this; - } - - /** - * Returns length of vector - * - * @return float - */ - public function length() - { - return sqrt( - pow( $this->x, 2 ) + - pow( $this->y, 2 ) - ); - } - - /** - * Multiplies vector with a scalar - * - * @param float $value - * @return void - */ - public function scalar( $value ) - { - $this->x *= $value; - $this->y *= $value; - - return $this; - } - - /** - * Calculates scalar product of two vectors - * - * @param ezcGraphCoordinate $vector - * @return void - */ - public function mul( ezcGraphCoordinate $vector ) - { - return $this->x * $vector->x + $this->y * $vector->y; - } - - /** - * Returns the angle between two vectors in radian - * - * @param ezcGraphCoordinate $vector - * @return float - */ - public function angle( ezcGraphCoordinate $vector ) - { - if ( !$vector instanceof ezcGraphVector ) - { - // Ensure beeing a vector for calling length() - $vector = ezcGraphVector::fromCoordinate( $vector ); - } - - $factor = $this->length() * $vector->length(); - - if ( $factor == 0 ) - { - return false; - } - else - { - return acos( $this->mul( $vector ) / $factor ); - } - } - - /** - * Adds a vector to another vector - * - * @param ezcGraphCoordinate $vector - * @return void - */ - public function add( ezcGraphCoordinate $vector ) - { - $this->x += $vector->x; - $this->y += $vector->y; - - return $this; - } - - /** - * Subtracts a vector from another vector - * - * @param ezcGraphCoordinate $vector - * @return void - */ - public function sub( ezcGraphCoordinate $vector ) - { - $this->x -= $vector->x; - $this->y -= $vector->y; - - return $this; - } - - /** - * Creates a vector from a coordinate object - * - * @param ezcGraphCoordinate $coordinate - * @return ezcGraphVector - */ - public static function fromCoordinate( ezcGraphCoordinate $coordinate ) - { - return new ezcGraphVector( $coordinate->x, $coordinate->y ); - } - - /** - * Transform vector using transformation matrix - * - * @param ezcGraphTransformation $transformation - * @return ezcGraphVector - */ - public function transform( ezcGraphTransformation $transformation ) - { - $result = $transformation->transformCoordinate( $this ); - - $this->x = $result->x; - $this->y = $result->y; - - return $this; - } -} - -?> +x; + $this->x = $this->y; + $this->y = -$tmp; + + return $this; + } + + /** + * Rotates vector to the right by 90 degrees + * + * @return void + */ + public function rotateClockwise() + { + $tmp = $this->x; + $this->x = -$this->y; + $this->y = $tmp; + + return $this; + } + + /** + * Unifies vector length to 1 + * + * @return void + */ + public function unify() + { + $length = $this->length(); + if ( $length == 0 ) + { + return $this; + } + + $this->x /= $length; + $this->y /= $length; + + return $this; + } + + /** + * Returns length of vector + * + * @return float + */ + public function length() + { + return sqrt( + pow( $this->x, 2 ) + + pow( $this->y, 2 ) + ); + } + + /** + * Multiplies vector with a scalar + * + * @param float $value + * @return void + */ + public function scalar( $value ) + { + $this->x *= $value; + $this->y *= $value; + + return $this; + } + + /** + * Calculates scalar product of two vectors + * + * @param ezcGraphCoordinate $vector + * @return void + */ + public function mul( ezcGraphCoordinate $vector ) + { + return $this->x * $vector->x + $this->y * $vector->y; + } + + /** + * Returns the angle between two vectors in radian + * + * @param ezcGraphCoordinate $vector + * @return float + */ + public function angle( ezcGraphCoordinate $vector ) + { + if ( !$vector instanceof ezcGraphVector ) + { + // Ensure beeing a vector for calling length() + $vector = ezcGraphVector::fromCoordinate( $vector ); + } + + $factor = $this->length() * $vector->length(); + + if ( $factor == 0 ) + { + return false; + } + else + { + return acos( $this->mul( $vector ) / $factor ); + } + } + + /** + * Adds a vector to another vector + * + * @param ezcGraphCoordinate $vector + * @return void + */ + public function add( ezcGraphCoordinate $vector ) + { + $this->x += $vector->x; + $this->y += $vector->y; + + return $this; + } + + /** + * Subtracts a vector from another vector + * + * @param ezcGraphCoordinate $vector + * @return void + */ + public function sub( ezcGraphCoordinate $vector ) + { + $this->x -= $vector->x; + $this->y -= $vector->y; + + return $this; + } + + /** + * Creates a vector from a coordinate object + * + * @param ezcGraphCoordinate $coordinate + * @return ezcGraphVector + */ + public static function fromCoordinate( ezcGraphCoordinate $coordinate ) + { + return new ezcGraphVector( $coordinate->x, $coordinate->y ); + } + + /** + * Transform vector using transformation matrix + * + * @param ezcGraphTransformation $transformation + * @return ezcGraphVector + */ + public function transform( ezcGraphTransformation $transformation ) + { + $result = $transformation->transformCoordinate( $this ); + + $this->x = $result->x; + $this->y = $result->y; + + return $this; + } +} + +?> diff --git a/library/ezc/Graph/src/options/cairo_driver.php b/library/ezc/Graph/src/options/cairo_driver.php index 85fee5f14f..7cf7a81cb8 100644 --- a/library/ezc/Graph/src/options/cairo_driver.php +++ b/library/ezc/Graph/src/options/cairo_driver.php @@ -1,100 +1,100 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->background->color = '#FFFFFFFF'; - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->driver = new ezcGraphCairoDriver(); - * - * // No options yet. - * - * $graph->render( 400, 200, 'tutorial_driver_cairo.png' ); - * - * - * @property float $imageMapResolution - * Degree step used to interpolate round image primitives by - * polygons for image maps - * @property float $circleResolution - * Resolution for circles, until I understand how to draw ellipses - * with SWFShape::curveTo() - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphCairoDriverOptions extends ezcGraphDriverOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['imageMapResolution'] = 10; - $this->properties['circleResolution'] = 2.; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'imageMapResolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['imageMapResolution'] = (int) $propertyValue; - break; - case 'circleResolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['circleResolution'] = (float) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->background->color = '#FFFFFFFF'; + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->driver = new ezcGraphCairoDriver(); + * + * // No options yet. + * + * $graph->render( 400, 200, 'tutorial_driver_cairo.png' ); + * + * + * @property float $imageMapResolution + * Degree step used to interpolate round image primitives by + * polygons for image maps + * @property float $circleResolution + * Resolution for circles, until I understand how to draw ellipses + * with SWFShape::curveTo() + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphCairoDriverOptions extends ezcGraphDriverOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['imageMapResolution'] = 10; + $this->properties['circleResolution'] = 2.; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'imageMapResolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['imageMapResolution'] = (int) $propertyValue; + break; + case 'circleResolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['circleResolution'] = (float) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/chart.php b/library/ezc/Graph/src/options/chart.php index 797908edc1..468e5f8dd2 100644 --- a/library/ezc/Graph/src/options/chart.php +++ b/library/ezc/Graph/src/options/chart.php @@ -1,107 +1,107 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzBlue(); - * $graph->title = 'Access statistics'; - * - * // Global font options - * $graph->options->font->name = 'serif'; - * - * // Special font options for sub elements - * $graph->title->background = '#EEEEEC'; - * $graph->title->font->name = 'sans-serif'; - * - * $graph->options->font->maxFontSize = 8; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->render( 400, 150, 'tutorial_chart_title.svg' ); - * - * - * @property int $width - * Width of the chart. - * @property int $height - * Height of the chart. - * @property ezcGraphFontOptions $font - * Font used in the graph. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphChartOptions extends ezcBaseOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['width'] = null; - $this->properties['height'] = null; - $this->properties['font'] = new ezcGraphFontOptions(); - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'width': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['width'] = (int) $propertyValue; - break; - case 'height': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['height'] = (int) $propertyValue; - break; - case 'font': - $this->properties['font']->path = $propertyValue; - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzBlue(); + * $graph->title = 'Access statistics'; + * + * // Global font options + * $graph->options->font->name = 'serif'; + * + * // Special font options for sub elements + * $graph->title->background = '#EEEEEC'; + * $graph->title->font->name = 'sans-serif'; + * + * $graph->options->font->maxFontSize = 8; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->render( 400, 150, 'tutorial_chart_title.svg' ); + * + * + * @property int $width + * Width of the chart. + * @property int $height + * Height of the chart. + * @property ezcGraphFontOptions $font + * Font used in the graph. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphChartOptions extends ezcBaseOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['width'] = null; + $this->properties['height'] = null; + $this->properties['font'] = new ezcGraphFontOptions(); + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'width': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['width'] = (int) $propertyValue; + break; + case 'height': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['height'] = (int) $propertyValue; + break; + case 'font': + $this->properties['font']->path = $propertyValue; + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/driver.php b/library/ezc/Graph/src/options/driver.php index 7311d79c44..4b646b5236 100644 --- a/library/ezc/Graph/src/options/driver.php +++ b/library/ezc/Graph/src/options/driver.php @@ -1,172 +1,172 @@ - - * require_once 'tutorial_autoload.php'; - * - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzBlue(); - * $graph->title = 'Access statistics'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * // Do not shorten strings automatically if they do not fit in the assigned - * // space with the minimum font size. - * $graph->driver->options->autoShortenString = false; - * - * $graph->render( 400, 150, 'tutorial_chart_title.svg' ); - * - * - * @property int $width - * Width of the chart. - * @property int $height - * Height of the chart. - * @property float $shadeCircularArc - * Percent to darken circular arcs at the sides - * @property float $lineSpacing - * Percent of font size used for line spacing - * @property int $font - * Font used in the graph. - * @property bool $autoShortenString - * Automatically shorten string if it do not fit into the available - * space, even with the minimum font size used. Deactivating this - * setting will result in ezcGraphFontRenderingException exceptions, - * informing you about the actual string which did not fit. - * @property string $autoShortenStringPostFix - * String to append to shortened strings, if there is enough space - * left for the postfix. - * - * @version //autogentag// - * @package Graph - */ -abstract class ezcGraphDriverOptions extends ezcBaseOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['width'] = null; - $this->properties['height'] = null; - - $this->properties['lineSpacing'] = .1; - $this->properties['shadeCircularArc'] = .5; - $this->properties['font'] = new ezcGraphFontOptions(); - $this->properties['font']->color = ezcGraphColor::fromHex( '#000000' ); - - $this->properties['autoShortenString'] = true; - $this->properties['autoShortenStringPostFix'] = '..'; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'width': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['width'] = (int) $propertyValue; - break; - case 'height': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['height'] = (int) $propertyValue; - break; - case 'lineSpacing': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['lineSpacing'] = (float) $propertyValue; - break; - case 'shadeCircularArc': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['shadeCircularArc'] = (float) $propertyValue; - break; - case 'font': - if ( $propertyValue instanceof ezcGraphFontOptions ) - { - $this->properties['font'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); - } - break; - case 'autoShortenString': - if ( is_bool( $propertyValue ) ) - { - $this->properties['autoShortenString'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' ); - } - break; - case 'autoShortenStringPostFix': - $this->properties['autoShortenStringPostFix'] = (string) $propertyValue; - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } -} - -?> + + * require_once 'tutorial_autoload.php'; + * + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzBlue(); + * $graph->title = 'Access statistics'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * // Do not shorten strings automatically if they do not fit in the assigned + * // space with the minimum font size. + * $graph->driver->options->autoShortenString = false; + * + * $graph->render( 400, 150, 'tutorial_chart_title.svg' ); + * + * + * @property int $width + * Width of the chart. + * @property int $height + * Height of the chart. + * @property float $shadeCircularArc + * Percent to darken circular arcs at the sides + * @property float $lineSpacing + * Percent of font size used for line spacing + * @property int $font + * Font used in the graph. + * @property bool $autoShortenString + * Automatically shorten string if it do not fit into the available + * space, even with the minimum font size used. Deactivating this + * setting will result in ezcGraphFontRenderingException exceptions, + * informing you about the actual string which did not fit. + * @property string $autoShortenStringPostFix + * String to append to shortened strings, if there is enough space + * left for the postfix. + * + * @version 1.4.3 + * @package Graph + */ +abstract class ezcGraphDriverOptions extends ezcBaseOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['width'] = null; + $this->properties['height'] = null; + + $this->properties['lineSpacing'] = .1; + $this->properties['shadeCircularArc'] = .5; + $this->properties['font'] = new ezcGraphFontOptions(); + $this->properties['font']->color = ezcGraphColor::fromHex( '#000000' ); + + $this->properties['autoShortenString'] = true; + $this->properties['autoShortenStringPostFix'] = '..'; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'width': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['width'] = (int) $propertyValue; + break; + case 'height': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['height'] = (int) $propertyValue; + break; + case 'lineSpacing': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['lineSpacing'] = (float) $propertyValue; + break; + case 'shadeCircularArc': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['shadeCircularArc'] = (float) $propertyValue; + break; + case 'font': + if ( $propertyValue instanceof ezcGraphFontOptions ) + { + $this->properties['font'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); + } + break; + case 'autoShortenString': + if ( is_bool( $propertyValue ) ) + { + $this->properties['autoShortenString'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' ); + } + break; + case 'autoShortenStringPostFix': + $this->properties['autoShortenStringPostFix'] = (string) $propertyValue; + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/flash_driver.php b/library/ezc/Graph/src/options/flash_driver.php index c9e5a51dc3..6c32bbb45a 100644 --- a/library/ezc/Graph/src/options/flash_driver.php +++ b/library/ezc/Graph/src/options/flash_driver.php @@ -1,107 +1,107 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->driver = new ezcGraphFlashDriver(); - * $graph->driver->options->compresion = 0; - * - * $graph->options->font = 'tutorial_font.fdb'; - * - * $graph->driver->options->compression = 7; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->render( 400, 200, 'tutorial_driver_flash.swf' ); - * - * - * @property int $compression - * Compression level used for generated flash file - * @see http://php.net/manual/en/function.swfmovie.save.php - * @property float $circleResolution - * Resolution for circles, until I understand how to draw ellipses - * with SWFShape::curveTo() - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphFlashDriverOptions extends ezcGraphDriverOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['compression'] = 9; - $this->properties['circleResolution'] = 2.; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'compression': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 9 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 9' ); - } - - $this->properties['compression'] = max( 0, min( 9, (int) $propertyValue ) ); - break; - case 'circleResolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['circleResolution'] = (float) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->driver = new ezcGraphFlashDriver(); + * $graph->driver->options->compresion = 0; + * + * $graph->options->font = 'tutorial_font.fdb'; + * + * $graph->driver->options->compression = 7; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->render( 400, 200, 'tutorial_driver_flash.swf' ); + * + * + * @property int $compression + * Compression level used for generated flash file + * @see http://php.net/manual/en/function.swfmovie.save.php + * @property float $circleResolution + * Resolution for circles, until I understand how to draw ellipses + * with SWFShape::curveTo() + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphFlashDriverOptions extends ezcGraphDriverOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['compression'] = 9; + $this->properties['circleResolution'] = 2.; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'compression': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 9 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 9' ); + } + + $this->properties['compression'] = max( 0, min( 9, (int) $propertyValue ) ); + break; + case 'circleResolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['circleResolution'] = (float) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/font.php b/library/ezc/Graph/src/options/font.php index f4cc281380..c8dedc0357 100644 --- a/library/ezc/Graph/src/options/font.php +++ b/library/ezc/Graph/src/options/font.php @@ -1,308 +1,312 @@ -options->font. This takes effect on all chart - * elements unless you intentionally access the font configuration of an - * individual chart element. The following example shows, how this works. - * - * - * $graph = new ezcGraphPieChart(); - * $graph->title = 'Access statistics'; - * - * // Set the maximum font size to 8 for all chart elements - * $graph->options->font->maxFontSize = 8; - * - * // Set the font size for the title independently to 14 - * $graph->title->font->maxFontSize = 14; - * - * // The following only affects all elements except the // title element, - * // which now has its own font configuration. - * $graph->options->font->name = 'serif'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * - * @property string $name - * Name of font. - * @property string $path - * Path to font file. - * @property int $type - * Type of used font. May be one of the following: - * - TTF_FONT Native TTF fonts - * - PS_FONT PostScript Type1 fonts - * - FT2_FONT FreeType 2 fonts - * The type is normally automatically detected when you set the path - * to the font file. - * @property float $minFontSize - * Minimum font size for displayed texts. - * @property float $maxFontSize - * Maximum font size for displayed texts. - * @property float $minimalUsedFont - * The minimal used font size for the current element group. This - * property is set by the driver to maintain this information and - * should not be used to configure the apperance of the chart. See - * $minFontSize instead. - * @property ezcGraphColor $color - * Font color. - * @property ezcGraphColor $background - * Background color. The actual area filled with the background color - * is influenced by the settings $padding and $minimizeBorder. - * @property ezcGraphColor $border - * Border color for the text. The distance between the text and - * border is defined by the properties $padding and $minimizeBorder. - * @property int $borderWidth - * With of the border. To enable the border you need to set the - * $border property to some color. - * @property int $padding - * Padding between text and border. - * @property bool $minimizeBorder - * Fit the border exactly around the text, or use the complete - * possible space. This setting is only relevant, when a border - * color has been set for the font. - * @property bool $textShadow - * Draw shadow for texts. The color of the shadow is defined in - * the property $textShadowColor. - * @property int $textShadowOffset - * Offset for text shadow. This defines the distance the shadow - * is moved to the bottom left relative from the text position. - * @property ezcGraphColor $textShadowColor - * Color of text shadow. If left at the default value "false"" - * the inverse color of the text color will be used. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphFontOptions extends ezcBaseOptions -{ - /** - * Indicates if path already has been checked for correct font - * - * @var bool - */ - protected $pathChecked = false; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['name'] = 'sans-serif'; -// $this->properties['path'] = 'Graph/tests/data/font.ttf'; - $this->properties['path'] = ''; - $this->properties['type'] = ezcGraph::TTF_FONT; - - $this->properties['minFontSize'] = 6; - $this->properties['maxFontSize'] = 96; - $this->properties['minimalUsedFont'] = 96; - $this->properties['color'] = ezcGraphColor::fromHex( '#000000' ); - - $this->properties['background'] = false; - $this->properties['border'] = false; - $this->properties['borderWidth'] = 1; - $this->properties['padding'] = 0; - $this->properties['minimizeBorder'] = true; - - $this->properties['textShadow'] = false; - $this->properties['textShadowOffset'] = 1; - $this->properties['textShadowColor'] = false; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'minFontSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' ); - } - - // Ensure min font size is smaller or equal max font size. - if ( $propertyValue > $this->properties['maxFontSize'] ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float <= ' . $this->properties['maxFontSize'] ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'maxFontSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' ); - } - - // Ensure max font size is greater or equal min font size. - if ( $propertyValue < $this->properties['minFontSize'] ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= ' . $this->properties['minFontSize'] ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'minimalUsedFont': - $propertyValue = (float) $propertyValue; - if ( $propertyValue < $this->minimalUsedFont ) - { - $this->properties['minimalUsedFont'] = $propertyValue; - } - break; - - case 'color': - case 'background': - case 'border': - case 'textShadowColor': - $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); - break; - - case 'borderWidth': - case 'padding': - case 'textShadowOffset': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - - case 'minimizeBorder': - case 'textShadow': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - $this->properties[$propertyName] = (bool) $propertyValue; - break; - - case 'name': - if ( is_string( $propertyValue ) ) - { - $this->properties['name'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' ); - } - break; - case 'path': - if ( is_file( $propertyValue ) && is_readable( $propertyValue ) ) - { - $this->properties['path'] = $propertyValue; - $parts = pathinfo( $this->properties['path'] ); - switch ( strtolower( $parts['extension'] ) ) - { - case 'fdb': - $this->properties['type'] = ezcGraph::PALM_FONT; - break; - case 'pfb': - $this->properties['type'] = ezcGraph::PS_FONT; - break; - case 'ttf': - $this->properties['type'] = ezcGraph::TTF_FONT; - break; - case 'svg': - $this->properties['type'] = ezcGraph::SVG_FONT; - $this->properties['name'] = ezcGraphSvgFont::getFontName( $propertyValue ); - break; - default: - throw new ezcGraphUnknownFontTypeException( $propertyValue, $parts['extension'] ); - } - $this->pathChecked = true; - } - else - { - throw new ezcBaseFileNotFoundException( $propertyValue, 'font' ); - } - break; - case 'type': - if ( is_int( $propertyValue ) ) - { - $this->properties['type'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' ); - } - break; - default: - throw new ezcBasePropertyNotFoundException( $propertyName ); - break; - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'textShadowColor': - // Use inverted font color if false - if ( $this->properties['textShadowColor'] === false ) - { - $this->properties['textShadowColor'] = $this->properties['color']->invert(); - } - - return $this->properties['textShadowColor']; - case 'path': - if ( $this->pathChecked === false ) - { - // Enforce call of path check - $this->__set( 'path', $this->properties['path'] ); - } - // No break to use parent return - default: - return parent::__get( $propertyName ); - } - } -} - -?> +options->font. This takes effect on all chart + * elements unless you intentionally access the font configuration of an + * individual chart element. The following example shows, how this works. + * + * + * $graph = new ezcGraphPieChart(); + * $graph->title = 'Access statistics'; + * + * // Set the maximum font size to 8 for all chart elements + * $graph->options->font->maxFontSize = 8; + * + * // Set the font size for the title independently to 14 + * $graph->title->font->maxFontSize = 14; + * + * // The following only affects all elements except the // title element, + * // which now has its own font configuration. + * // + * // Keep in mind that the specified font is driver specific. A pure name + * // works for the SVG driver, used here. The GD driver for example + * // requires a path to a TTF file. + * $graph->options->font->name = 'serif'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * + * @property string $name + * Name of font. + * @property string $path + * Path to font file. + * @property int $type + * Type of used font. May be one of the following: + * - TTF_FONT Native TTF fonts + * - PS_FONT PostScript Type1 fonts + * - FT2_FONT FreeType 2 fonts + * The type is normally automatically detected when you set the path + * to the font file. + * @property float $minFontSize + * Minimum font size for displayed texts. + * @property float $maxFontSize + * Maximum font size for displayed texts. + * @property float $minimalUsedFont + * The minimal used font size for the current element group. This + * property is set by the driver to maintain this information and + * should not be used to configure the apperance of the chart. See + * $minFontSize instead. + * @property ezcGraphColor $color + * Font color. + * @property ezcGraphColor $background + * Background color. The actual area filled with the background color + * is influenced by the settings $padding and $minimizeBorder. + * @property ezcGraphColor $border + * Border color for the text. The distance between the text and + * border is defined by the properties $padding and $minimizeBorder. + * @property int $borderWidth + * With of the border. To enable the border you need to set the + * $border property to some color. + * @property int $padding + * Padding between text and border. + * @property bool $minimizeBorder + * Fit the border exactly around the text, or use the complete + * possible space. This setting is only relevant, when a border + * color has been set for the font. + * @property bool $textShadow + * Draw shadow for texts. The color of the shadow is defined in + * the property $textShadowColor. + * @property int $textShadowOffset + * Offset for text shadow. This defines the distance the shadow + * is moved to the bottom left relative from the text position. + * @property ezcGraphColor $textShadowColor + * Color of text shadow. If left at the default value "false"" + * the inverse color of the text color will be used. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphFontOptions extends ezcBaseOptions +{ + /** + * Indicates if path already has been checked for correct font + * + * @var bool + */ + protected $pathChecked = false; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['name'] = 'sans-serif'; +// $this->properties['path'] = 'Graph/tests/data/font.ttf'; + $this->properties['path'] = ''; + $this->properties['type'] = ezcGraph::TTF_FONT; + + $this->properties['minFontSize'] = 6; + $this->properties['maxFontSize'] = 96; + $this->properties['minimalUsedFont'] = 96; + $this->properties['color'] = ezcGraphColor::fromHex( '#000000' ); + + $this->properties['background'] = false; + $this->properties['border'] = false; + $this->properties['borderWidth'] = 1; + $this->properties['padding'] = 0; + $this->properties['minimizeBorder'] = true; + + $this->properties['textShadow'] = false; + $this->properties['textShadowOffset'] = 1; + $this->properties['textShadowColor'] = false; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'minFontSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' ); + } + + // Ensure min font size is smaller or equal max font size. + if ( $propertyValue > $this->properties['maxFontSize'] ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float <= ' . $this->properties['maxFontSize'] ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'maxFontSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' ); + } + + // Ensure max font size is greater or equal min font size. + if ( $propertyValue < $this->properties['minFontSize'] ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= ' . $this->properties['minFontSize'] ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'minimalUsedFont': + $propertyValue = (float) $propertyValue; + if ( $propertyValue < $this->minimalUsedFont ) + { + $this->properties['minimalUsedFont'] = $propertyValue; + } + break; + + case 'color': + case 'background': + case 'border': + case 'textShadowColor': + $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); + break; + + case 'borderWidth': + case 'padding': + case 'textShadowOffset': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + + case 'minimizeBorder': + case 'textShadow': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + $this->properties[$propertyName] = (bool) $propertyValue; + break; + + case 'name': + if ( is_string( $propertyValue ) ) + { + $this->properties['name'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' ); + } + break; + case 'path': + if ( is_file( $propertyValue ) && is_readable( $propertyValue ) ) + { + $this->properties['path'] = $propertyValue; + $parts = pathinfo( $this->properties['path'] ); + switch ( strtolower( $parts['extension'] ) ) + { + case 'fdb': + $this->properties['type'] = ezcGraph::PALM_FONT; + break; + case 'pfb': + $this->properties['type'] = ezcGraph::PS_FONT; + break; + case 'ttf': + $this->properties['type'] = ezcGraph::TTF_FONT; + break; + case 'svg': + $this->properties['type'] = ezcGraph::SVG_FONT; + $this->properties['name'] = ezcGraphSvgFont::getFontName( $propertyValue ); + break; + default: + throw new ezcGraphUnknownFontTypeException( $propertyValue, $parts['extension'] ); + } + $this->pathChecked = true; + } + else + { + throw new ezcBaseFileNotFoundException( $propertyValue, 'font' ); + } + break; + case 'type': + if ( is_int( $propertyValue ) ) + { + $this->properties['type'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' ); + } + break; + default: + throw new ezcBasePropertyNotFoundException( $propertyName ); + break; + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'textShadowColor': + // Use inverted font color if false + if ( $this->properties['textShadowColor'] === false ) + { + $this->properties['textShadowColor'] = $this->properties['color']->invert(); + } + + return $this->properties['textShadowColor']; + case 'path': + if ( $this->pathChecked === false ) + { + // Enforce call of path check + $this->__set( 'path', $this->properties['path'] ); + } + // No break to use parent return + default: + return parent::__get( $propertyName ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/gd_driver.php b/library/ezc/Graph/src/options/gd_driver.php index 99379a1bd0..d03c1bfbca 100644 --- a/library/ezc/Graph/src/options/gd_driver.php +++ b/library/ezc/Graph/src/options/gd_driver.php @@ -1,181 +1,181 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzGreen(); - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->driver = new ezcGraphGdDriver(); - * $graph->options->font = 'tutorial_font.ttf'; - * - * // Generate a Jpeg with lower quality. The default settings result in a better - * // quality image - * $graph->driver->options->supersampling = 1; - * $graph->driver->options->jpegQuality = 100; - * $graph->driver->options->imageFormat = IMG_JPEG; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' ); - * - * - * @property int $imageFormat - * Type of generated image. - * Should be one of those: IMG_PNG, IMG_JPEG - * @property int $jpegQuality - * Quality of generated jpeg - * @property int $detail - * Count of degrees to render one polygon for in circular arcs - * @property int $supersampling - * Factor of supersampling used to simulate antialiasing - * @property string $background - * Background image to put the graph on - * @property string $resampleFunction - * Function used to resample / resize images - * @property bool $forceNativeTTF - * Force use of native ttf functions instead of free type 2 - * @property float $imageMapResolution - * Degree step used to interpolate round image primitives by - * polygons for image maps - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphGdDriverOptions extends ezcGraphDriverOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['imageFormat'] = IMG_PNG; - $this->properties['jpegQuality'] = 70; - $this->properties['detail'] = 1; - $this->properties['shadeCircularArc'] = .5; - $this->properties['supersampling'] = 2; - $this->properties['background'] = false; - $this->properties['resampleFunction'] = 'imagecopyresampled'; - $this->properties['forceNativeTTF'] = false; - $this->properties['imageMapResolution'] = 10; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'imageFormat': - if ( imagetypes() & $propertyValue ) - { - $this->properties['imageFormat'] = (int) $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'Unsupported image type.' ); - } - break; - case 'jpegQuality': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 100 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 100' ); - } - - $this->properties['jpegQuality'] = (int) $propertyValue; - break; - case 'detail': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['detail'] = (int) $propertyValue; - break; - case 'supersampling': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['supersampling'] = (int) $propertyValue; - break; - case 'background': - if ( $propertyValue === false || - ( is_file( $propertyValue ) && is_readable( $propertyValue ) ) ) - { - $this->properties['background'] = realpath( $propertyValue ); - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'readable file' ); - } - break; - case 'resampleFunction': - if ( ezcBaseFeatures::hasFunction( $propertyValue ) ) - { - $this->properties['resampleFunction'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'function' ); - } - break; - case 'forceNativeTTF': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['forceNativeTTF'] = (bool) $propertyValue; - break; - case 'imageMapResolution': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties['imageMapResolution'] = (int) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzGreen(); + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->driver = new ezcGraphGdDriver(); + * $graph->options->font = 'tutorial_font.ttf'; + * + * // Generate a Jpeg with lower quality. The default settings result in a better + * // quality image + * $graph->driver->options->supersampling = 1; + * $graph->driver->options->jpegQuality = 100; + * $graph->driver->options->imageFormat = IMG_JPEG; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' ); + * + * + * @property int $imageFormat + * Type of generated image. + * Should be one of those: IMG_PNG, IMG_JPEG + * @property int $jpegQuality + * Quality of generated jpeg + * @property int $detail + * Count of degrees to render one polygon for in circular arcs + * @property int $supersampling + * Factor of supersampling used to simulate antialiasing + * @property string $background + * Background image to put the graph on + * @property string $resampleFunction + * Function used to resample / resize images + * @property bool $forceNativeTTF + * Force use of native ttf functions instead of free type 2 + * @property float $imageMapResolution + * Degree step used to interpolate round image primitives by + * polygons for image maps + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphGdDriverOptions extends ezcGraphDriverOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['imageFormat'] = IMG_PNG; + $this->properties['jpegQuality'] = 70; + $this->properties['detail'] = 1; + $this->properties['shadeCircularArc'] = .5; + $this->properties['supersampling'] = 2; + $this->properties['background'] = false; + $this->properties['resampleFunction'] = 'imagecopyresampled'; + $this->properties['forceNativeTTF'] = false; + $this->properties['imageMapResolution'] = 10; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'imageFormat': + if ( imagetypes() & $propertyValue ) + { + $this->properties['imageFormat'] = (int) $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'Unsupported image type.' ); + } + break; + case 'jpegQuality': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 100 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 100' ); + } + + $this->properties['jpegQuality'] = (int) $propertyValue; + break; + case 'detail': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['detail'] = (int) $propertyValue; + break; + case 'supersampling': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['supersampling'] = (int) $propertyValue; + break; + case 'background': + if ( $propertyValue === false || + ( is_file( $propertyValue ) && is_readable( $propertyValue ) ) ) + { + $this->properties['background'] = realpath( $propertyValue ); + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'readable file' ); + } + break; + case 'resampleFunction': + if ( ezcBaseFeatures::hasFunction( $propertyValue ) ) + { + $this->properties['resampleFunction'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'function' ); + } + break; + case 'forceNativeTTF': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['forceNativeTTF'] = (bool) $propertyValue; + break; + case 'imageMapResolution': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties['imageMapResolution'] = (int) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/line_chart.php b/library/ezc/Graph/src/options/line_chart.php index db9f11ab22..db83c186bc 100644 --- a/library/ezc/Graph/src/options/line_chart.php +++ b/library/ezc/Graph/src/options/line_chart.php @@ -1,209 +1,209 @@ - - * $graph = new ezcGraphLineChart(); - * $graph->title = 'Wikipedia articles'; - * - * $graph->options->fillLines = 220; - * $graph->options->lineThickness = 3; - * - * // Add data - * foreach ( $wikidata as $language => $data ) - * { - * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); - * } - * - * $graph->render( 400, 150, 'tutorial_line_chart.svg' ); - * - * - * @property float $lineThickness - * Thickness of chart lines - * @property mixed $fillLines - * Status wheather the space between line and axis should get filled. - * - FALSE to not fill the space at all. - * - (int) Opacity used to fill up the space with the lines color. - * @property int $symbolSize - * Size of symbols in line chart. - * @property ezcGraphFontOptions $highlightFont - * Font configuration for highlight tests - * @property int $highlightSize - * Size of highlight blocks - * @property bool $highlightLines - * If true, it adds lines to highlight the values position on the - * axis. - * @property true $stackBars - * Stack bars - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphLineChartOptions extends ezcGraphChartOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['lineThickness'] = 1; - $this->properties['fillLines'] = false; - $this->properties['symbolSize'] = 8; - $this->properties['highlightFont'] = new ezcGraphFontOptions(); - $this->properties['highlightFontCloned'] = false; - $this->properties['highlightSize'] = 14; - $this->properties['highlightXOffset'] = 0; - $this->properties['highlightYOffset'] = 0; - $this->properties['highlightLines'] = false; - $this->properties['stackBars'] = false; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'lineThickness': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - case 'symbolSize': - case 'highlightSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - $this->properties[$propertyName] = (int) $propertyValue; - break; - case 'highlightXOffset': - case 'highlightYOffset': - if ( !is_numeric ( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' ); - } - $this->properties[$propertyName] = (int) $propertyValue; - break; - case 'fillLines': - if ( ( $propertyValue !== false ) && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 255 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' ); - } - - $this->properties[$propertyName] = ( - $propertyValue === false - ? false - : (int) $propertyValue ); - break; - case 'highlightFont': - if ( $propertyValue instanceof ezcGraphFontOptions ) - { - $this->properties['highlightFont'] = $propertyValue; - } - elseif ( is_string( $propertyValue ) ) - { - if ( !$this->properties['highlightFontCloned'] ) - { - $this->properties['highlightFont'] = clone $this->font; - $this->properties['highlightFontCloned'] = true; - } - - $this->properties['highlightFont']->path = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); - } - break; - $this->properties['highlightSize'] = max( 1, (int) $propertyValue ); - break; - case 'highlightLines': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['highlightLines'] = $propertyValue; - break; - case 'stackBars': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['stackBars'] = $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'highlightFont': - // Clone font configuration when requested for this element - if ( !$this->properties['highlightFontCloned'] ) - { - $this->properties['highlightFont'] = clone $this->properties['font']; - $this->properties['highlightFontCloned'] = true; - } - return $this->properties['highlightFont']; - default: - return parent::__get( $propertyName ); - } - } -} - -?> + + * $graph = new ezcGraphLineChart(); + * $graph->title = 'Wikipedia articles'; + * + * $graph->options->fillLines = 220; + * $graph->options->lineThickness = 3; + * + * // Add data + * foreach ( $wikidata as $language => $data ) + * { + * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); + * } + * + * $graph->render( 400, 150, 'tutorial_line_chart.svg' ); + * + * + * @property float $lineThickness + * Thickness of chart lines + * @property mixed $fillLines + * Status wheather the space between line and axis should get filled. + * - FALSE to not fill the space at all. + * - (int) Opacity used to fill up the space with the lines color. + * @property int $symbolSize + * Size of symbols in line chart. + * @property ezcGraphFontOptions $highlightFont + * Font configuration for highlight tests + * @property int $highlightSize + * Size of highlight blocks + * @property bool $highlightLines + * If true, it adds lines to highlight the values position on the + * axis. + * @property true $stackBars + * Stack bars + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphLineChartOptions extends ezcGraphChartOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['lineThickness'] = 1; + $this->properties['fillLines'] = false; + $this->properties['symbolSize'] = 8; + $this->properties['highlightFont'] = new ezcGraphFontOptions(); + $this->properties['highlightFontCloned'] = false; + $this->properties['highlightSize'] = 14; + $this->properties['highlightXOffset'] = 0; + $this->properties['highlightYOffset'] = 0; + $this->properties['highlightLines'] = false; + $this->properties['stackBars'] = false; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'lineThickness': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + case 'symbolSize': + case 'highlightSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + $this->properties[$propertyName] = (int) $propertyValue; + break; + case 'highlightXOffset': + case 'highlightYOffset': + if ( !is_numeric ( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' ); + } + $this->properties[$propertyName] = (int) $propertyValue; + break; + case 'fillLines': + if ( ( $propertyValue !== false ) && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 255 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' ); + } + + $this->properties[$propertyName] = ( + $propertyValue === false + ? false + : (int) $propertyValue ); + break; + case 'highlightFont': + if ( $propertyValue instanceof ezcGraphFontOptions ) + { + $this->properties['highlightFont'] = $propertyValue; + } + elseif ( is_string( $propertyValue ) ) + { + if ( !$this->properties['highlightFontCloned'] ) + { + $this->properties['highlightFont'] = clone $this->font; + $this->properties['highlightFontCloned'] = true; + } + + $this->properties['highlightFont']->path = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); + } + break; + $this->properties['highlightSize'] = max( 1, (int) $propertyValue ); + break; + case 'highlightLines': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['highlightLines'] = $propertyValue; + break; + case 'stackBars': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['stackBars'] = $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'highlightFont': + // Clone font configuration when requested for this element + if ( !$this->properties['highlightFontCloned'] ) + { + $this->properties['highlightFont'] = clone $this->properties['font']; + $this->properties['highlightFontCloned'] = true; + } + return $this->properties['highlightFont']; + default: + return parent::__get( $propertyName ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/odometer_chart.php b/library/ezc/Graph/src/options/odometer_chart.php index 9950bb2589..c97edff496 100644 --- a/library/ezc/Graph/src/options/odometer_chart.php +++ b/library/ezc/Graph/src/options/odometer_chart.php @@ -1,113 +1,113 @@ - - * $graph = new ezcGraphOdoMeterChart(); - * - * $graph->data['Test'] = new ezcGraphArrayDataSet( array( 0, 1, 23, 30 ) ); - * - * $graph->options->odometerHeight = .3; - * $graph->options->borderColor = '#2e3436'; - * - * $graph->render( 150, 50, 'odometer.svg' ); - * - * - * @property ezcGraphColor $borderColor - * Color of border around odometer chart - * @property int $borderWidth - * Width of border around odometer chart - * @property ezcGraphColor $startColor - * Start color of grdient used as the odometer chart background. - * @property ezcGraphColor $endColor - * End color of grdient used as the odometer chart background. - * @property int $markerWidth - * Width of odometer markers - * @property float $odometerHeight - * Height consumed by odometer chart - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphOdometerChartOptions extends ezcGraphChartOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['borderColor'] = ezcGraphColor::create( '#000000' ); - $this->properties['borderWidth'] = 0; - - $this->properties['startColor'] = ezcGraphColor::create( '#4e9a06A0' ); - $this->properties['endColor'] = ezcGraphColor::create( '#A40000A0' ); - - $this->properties['markerWidth'] = 2; - - $this->properties['odometerHeight'] = 0.5; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'borderWidth': - case 'markerWidth': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - - case 'borderColor': - case 'startColor': - case 'endColor': - $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); - break; - - case 'odometerHeight': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - default: - return parent::__set( $propertyName, $propertyValue ); - } - } -} - -?> + + * $graph = new ezcGraphOdoMeterChart(); + * + * $graph->data['Test'] = new ezcGraphArrayDataSet( array( 0, 1, 23, 30 ) ); + * + * $graph->options->odometerHeight = .3; + * $graph->options->borderColor = '#2e3436'; + * + * $graph->render( 150, 50, 'odometer.svg' ); + * + * + * @property ezcGraphColor $borderColor + * Color of border around odometer chart + * @property int $borderWidth + * Width of border around odometer chart + * @property ezcGraphColor $startColor + * Start color of grdient used as the odometer chart background. + * @property ezcGraphColor $endColor + * End color of grdient used as the odometer chart background. + * @property int $markerWidth + * Width of odometer markers + * @property float $odometerHeight + * Height consumed by odometer chart + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphOdometerChartOptions extends ezcGraphChartOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['borderColor'] = ezcGraphColor::create( '#000000' ); + $this->properties['borderWidth'] = 0; + + $this->properties['startColor'] = ezcGraphColor::create( '#4e9a06A0' ); + $this->properties['endColor'] = ezcGraphColor::create( '#A40000A0' ); + + $this->properties['markerWidth'] = 2; + + $this->properties['odometerHeight'] = 0.5; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'borderWidth': + case 'markerWidth': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + + case 'borderColor': + case 'startColor': + case 'endColor': + $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); + break; + + case 'odometerHeight': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + default: + return parent::__set( $propertyName, $propertyValue ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/pie_chart.php b/library/ezc/Graph/src/options/pie_chart.php index a1244174c9..97f109a0a5 100644 --- a/library/ezc/Graph/src/options/pie_chart.php +++ b/library/ezc/Graph/src/options/pie_chart.php @@ -1,151 +1,151 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzRed(); - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->options->label = '%1$s (%3$.1f)'; - * $graph->options->percentThreshold = .05; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * $graph->data['Access statistics']->highlight['Explorer'] = true; - * - * $graph->render( 400, 150, 'tutorial_pie_chart_options.svg' ); - * - * - * @property string $label - * String used to label pies - * %1$s Name of pie - * %2$d Value of pie - * %3$.1f Percentage - * @property callback $labelCallback - * Callback function to format pie chart labels. - * Function will receive 3 parameters: - * string function( label, value, percent ) - * @property float $sum - * Fixed sum of values. This should be used for incomplete pie - * charts. - * @property float $percentThreshold - * Values with a lower percentage value are aggregated. - * @property float $absoluteThreshold - * Values with a lower absolute value are aggregated. - * @property string $summarizeCaption - * Caption for values summarized because they are lower then the - * configured tresh hold. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphPieChartOptions extends ezcGraphChartOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['label'] = '%1$s: %2$d (%3$.1f%%)'; - $this->properties['labelCallback'] = null; - $this->properties['sum'] = false; - - $this->properties['percentThreshold'] = .0; - $this->properties['absoluteThreshold'] = .0; - $this->properties['summarizeCaption'] = 'Misc'; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'label': - $this->properties['label'] = (string) $propertyValue; - break; - case 'labelCallback': - if ( is_callable( $propertyValue ) ) - { - $this->properties['labelCallback'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' ); - } - break; - case 'sum': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['sum'] = (float) $propertyValue; - break; - case 'percentThreshold': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['percentThreshold'] = (float) $propertyValue; - break; - case 'absoluteThreshold': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['absoluteThreshold'] = (float) $propertyValue; - break; - case 'summarizeCaption': - $this->properties['summarizeCaption'] = (string) $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzRed(); + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->options->label = '%1$s (%3$.1f)'; + * $graph->options->percentThreshold = .05; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * $graph->data['Access statistics']->highlight['Explorer'] = true; + * + * $graph->render( 400, 150, 'tutorial_pie_chart_options.svg' ); + * + * + * @property string $label + * String used to label pies + * %1$s Name of pie + * %2$d Value of pie + * %3$.1f Percentage + * @property callback $labelCallback + * Callback function to format pie chart labels. + * Function will receive 3 parameters: + * string function( label, value, percent ) + * @property float $sum + * Fixed sum of values. This should be used for incomplete pie + * charts. + * @property float $percentThreshold + * Values with a lower percentage value are aggregated. + * @property float $absoluteThreshold + * Values with a lower absolute value are aggregated. + * @property string $summarizeCaption + * Caption for values summarized because they are lower then the + * configured tresh hold. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphPieChartOptions extends ezcGraphChartOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['label'] = '%1$s: %2$d (%3$.1f%%)'; + $this->properties['labelCallback'] = null; + $this->properties['sum'] = false; + + $this->properties['percentThreshold'] = .0; + $this->properties['absoluteThreshold'] = .0; + $this->properties['summarizeCaption'] = 'Misc'; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'label': + $this->properties['label'] = (string) $propertyValue; + break; + case 'labelCallback': + if ( is_callable( $propertyValue ) ) + { + $this->properties['labelCallback'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' ); + } + break; + case 'sum': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['sum'] = (float) $propertyValue; + break; + case 'percentThreshold': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['percentThreshold'] = (float) $propertyValue; + break; + case 'absoluteThreshold': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['absoluteThreshold'] = (float) $propertyValue; + break; + case 'summarizeCaption': + $this->properties['summarizeCaption'] = (string) $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/radar_chart.php b/library/ezc/Graph/src/options/radar_chart.php index da90a6bab5..8221f1e1cb 100644 --- a/library/ezc/Graph/src/options/radar_chart.php +++ b/library/ezc/Graph/src/options/radar_chart.php @@ -1,172 +1,172 @@ - - * $wikidata = include 'tutorial_wikipedia_data.php'; - * - * $graph = new ezcGraphRadarChart(); - * $graph->title = 'Wikipedia articles'; - * - * $graph->options->fillLines = 220; - * - * // Add data - * foreach ( $wikidata as $language => $data ) - * { - * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); - * $graph->data[$language][] = reset( $data ); - * } - * - * $graph->render( 400, 150, 'tutorial_radar_chart.svg' ); - * - * - * @property float $lineThickness - * Theickness of chart lines - * @property mixed $fillLines - * Status wheather the space between line and axis should get filled. - * - FALSE to not fill the space at all. - * - (int) Opacity used to fill up the space with the lines color. - * @property int $symbolSize - * Size of symbols in line chart. - * @property ezcGraphFontOptions $highlightFont - * Font configuration for highlight tests - * @property int $highlightSize - * Size of highlight blocks - * @property bool $highlightRadars - * If true, it adds lines to highlight the values position on the - * axis. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphRadarChartOptions extends ezcGraphChartOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['lineThickness'] = 1; - $this->properties['fillLines'] = false; - $this->properties['symbolSize'] = 8; - $this->properties['highlightFont'] = new ezcGraphFontOptions(); - $this->properties['highlightFontCloned'] = false; - $this->properties['highlightSize'] = 14; - $this->properties['highlightRadars'] = false; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'lineThickness': - case 'symbolSize': - case 'highlightSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - case 'fillLines': - if ( ( $propertyValue !== false ) && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 255 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' ); - } - - $this->properties[$propertyName] = ( - $propertyValue === false - ? false - : (int) $propertyValue ); - break; - case 'highlightFont': - if ( $propertyValue instanceof ezcGraphFontOptions ) - { - $this->properties['highlightFont'] = $propertyValue; - } - elseif ( is_string( $propertyValue ) ) - { - if ( !$this->properties['highlightFontCloned'] ) - { - $this->properties['highlightFont'] = clone $this->font; - $this->properties['highlightFontCloned'] = true; - } - - $this->properties['highlightFont']->path = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); - } - break; - $this->properties['highlightSize'] = max( 1, (int) $propertyValue ); - break; - case 'highlightRadars': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['highlightRadars'] = $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'highlightFont': - // Clone font configuration when requested for this element - if ( !$this->properties['highlightFontCloned'] ) - { - $this->properties['highlightFont'] = clone $this->properties['font']; - $this->properties['highlightFontCloned'] = true; - } - return $this->properties['highlightFont']; - default: - return parent::__get( $propertyName ); - } - } -} - -?> + + * $wikidata = include 'tutorial_wikipedia_data.php'; + * + * $graph = new ezcGraphRadarChart(); + * $graph->title = 'Wikipedia articles'; + * + * $graph->options->fillLines = 220; + * + * // Add data + * foreach ( $wikidata as $language => $data ) + * { + * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); + * $graph->data[$language][] = reset( $data ); + * } + * + * $graph->render( 400, 150, 'tutorial_radar_chart.svg' ); + * + * + * @property float $lineThickness + * Theickness of chart lines + * @property mixed $fillLines + * Status wheather the space between line and axis should get filled. + * - FALSE to not fill the space at all. + * - (int) Opacity used to fill up the space with the lines color. + * @property int $symbolSize + * Size of symbols in line chart. + * @property ezcGraphFontOptions $highlightFont + * Font configuration for highlight tests + * @property int $highlightSize + * Size of highlight blocks + * @property bool $highlightRadars + * If true, it adds lines to highlight the values position on the + * axis. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphRadarChartOptions extends ezcGraphChartOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['lineThickness'] = 1; + $this->properties['fillLines'] = false; + $this->properties['symbolSize'] = 8; + $this->properties['highlightFont'] = new ezcGraphFontOptions(); + $this->properties['highlightFontCloned'] = false; + $this->properties['highlightSize'] = 14; + $this->properties['highlightRadars'] = false; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'lineThickness': + case 'symbolSize': + case 'highlightSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + case 'fillLines': + if ( ( $propertyValue !== false ) && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 255 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' ); + } + + $this->properties[$propertyName] = ( + $propertyValue === false + ? false + : (int) $propertyValue ); + break; + case 'highlightFont': + if ( $propertyValue instanceof ezcGraphFontOptions ) + { + $this->properties['highlightFont'] = $propertyValue; + } + elseif ( is_string( $propertyValue ) ) + { + if ( !$this->properties['highlightFontCloned'] ) + { + $this->properties['highlightFont'] = clone $this->font; + $this->properties['highlightFontCloned'] = true; + } + + $this->properties['highlightFont']->path = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' ); + } + break; + $this->properties['highlightSize'] = max( 1, (int) $propertyValue ); + break; + case 'highlightRadars': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['highlightRadars'] = $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'highlightFont': + // Clone font configuration when requested for this element + if ( !$this->properties['highlightFontCloned'] ) + { + $this->properties['highlightFont'] = clone $this->properties['font']; + $this->properties['highlightFontCloned'] = true; + } + return $this->properties['highlightFont']; + default: + return parent::__get( $propertyName ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/renderer.php b/library/ezc/Graph/src/options/renderer.php index c662e05d9a..7f2e6a5ad7 100644 --- a/library/ezc/Graph/src/options/renderer.php +++ b/library/ezc/Graph/src/options/renderer.php @@ -1,248 +1,248 @@ - - * $wikidata = include 'tutorial_wikipedia_data.php'; - * - * $graph = new ezcGraphBarChart(); - * $graph->title = 'Wikipedia articles'; - * - * // Add data - * foreach ( $wikidata as $language => $data ) - * { - * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); - * } - * - * // $graph->renderer = new ezcGraphRenderer2d(); - * - * $graph->renderer->options->barMargin = .2; - * $graph->renderer->options->barPadding = .2; - * - * $graph->renderer->options->dataBorder = 0; - * - * $graph->render( 400, 150, 'tutorial_bar_chart_options.svg' ); - * - * - * For additional options, which are special to some chart type you may - * also want to check the option classes for the repective chart type you - * are using and the elements of the chart. The chart type dependant option - * classes are: - * - * - ezcGraphLineChartOptions - * - ezcGraphPieChartOptions - * - ezcGraphRadarChartOptions - * - * There may be additional options dependant on the renderer you are using. - * You may want to check the extensions of this class: - * - * - ezcGraphRenderer2dOptions - * - ezcGraphRenderer3dOptions - * - * @property float $maxLabelHeight - * Percent of chart height used as maximum height for pie chart - * labels. - * @property bool $showSymbol - * Indicates wheather to show the line between pie elements and - * labels. - * @property float $symbolSize - * Size of symbols used to connect a label with a pie. - * @property float $moveOut - * Percent to move pie chart elements out of the middle on highlight. - * @property int $titlePosition - * Position of title in a box. - * @property int $titleAlignement - * Alignement of box titles. - * @property float $dataBorder - * Factor to darken border of data elements, like lines, bars and - * pie segments. - * @property float $barMargin - * Percentual distance between bar blocks. - * @property float $barPadding - * Percentual distance between bars. - * @property float $pieChartOffset - * Offset for starting with first pie chart segment in degrees. - * @property float $legendSymbolGleam - * Opacity of gleam in legend symbols - * @property float $legendSymbolGleamSize - * Size of gleam in legend symbols - * @property float $legendSymbolGleamColor - * Color of gleam in legend symbols - * @property float $pieVerticalSize - * Percent of vertical space used for maximum pie chart size. - * @property float $pieHorizontalSize - * Percent of horizontal space used for maximum pie chart size. - * @property float $pieChartSymbolColor - * Color of pie chart symbols - * @property float $pieChartGleam - * Enhance pie chart with gleam on top. - * @property float $pieChartGleamColor - * Color used for gleam on pie charts. - * @property float $pieChartGleamBorder - * Do not draw gleam on an outer border of this size. - * @property bool $syncAxisFonts - * Synchronize fonts of axis. With the defaut true value, the only - * the fonts of the yAxis will be used. - * @property bool $axisEndStyle - * Style of axis end markers. Defauls to arrow heads, but you may - * also use all symbol constants defined ein the ezcGraph class, - * especially ezcGraph::NO_SYMBOL. - * @property bool $shortAxis - * Defines wheather to render the axis extending the chart boundings - * or stop them at the chart boundings. Deafults to false. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphRendererOptions extends ezcGraphChartOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['maxLabelHeight'] = .10; - $this->properties['showSymbol'] = true; - $this->properties['symbolSize'] = 6; - $this->properties['moveOut'] = .1; - $this->properties['titlePosition'] = ezcGraph::TOP; - $this->properties['titleAlignement'] = ezcGraph::MIDDLE | ezcGraph::CENTER; - $this->properties['dataBorder'] = .5; - $this->properties['barMargin'] = .1; - $this->properties['barPadding'] = .05; - $this->properties['pieChartOffset'] = 0; - $this->properties['pieChartSymbolColor'] = ezcGraphColor::fromHex( '#000000' ); - $this->properties['pieChartGleam'] = false; - $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); - $this->properties['pieChartGleamBorder'] = 0; - $this->properties['legendSymbolGleam'] = false; - $this->properties['legendSymbolGleamSize'] = .9; - $this->properties['legendSymbolGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); - $this->properties['pieVerticalSize'] = .5; - $this->properties['pieHorizontalSize'] = .25; - $this->properties['syncAxisFonts'] = true; - $this->properties['axisEndStyle'] = ezcGraph::ARROW; - $this->properties['shortAxis'] = false; - - parent::__construct( $options ); - } - - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'dataBorder': - case 'pieChartGleam': - case 'legendSymbolGleam': - if ( $propertyValue !== false && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' ); - } - - $this->properties[$propertyName] = ( - $propertyValue === false - ? false - : (float) $propertyValue ); - break; - - case 'maxLabelHeight': - case 'moveOut': - case 'barMargin': - case 'barPadding': - case 'legendSymbolGleamSize': - case 'pieVerticalSize': - case 'pieHorizontalSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'symbolSize': - case 'titlePosition': - case 'titleAlignement': - case 'pieChartGleamBorder': - case 'axisEndStyle': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); - } - - $this->properties[$propertyName] = (int) $propertyValue; - break; - - case 'showSymbol': - case 'syncAxisFonts': - case 'shortAxis': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - $this->properties[$propertyName] = (bool) $propertyValue; - break; - - case 'pieChartOffset': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 360 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 360' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'pieChartSymbolColor': - case 'pieChartGleamColor': - case 'legendSymbolGleamColor': - $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); - break; - - default: - return parent::__set( $propertyName, $propertyValue ); - } - } -} - -?> + + * $wikidata = include 'tutorial_wikipedia_data.php'; + * + * $graph = new ezcGraphBarChart(); + * $graph->title = 'Wikipedia articles'; + * + * // Add data + * foreach ( $wikidata as $language => $data ) + * { + * $graph->data[$language] = new ezcGraphArrayDataSet( $data ); + * } + * + * // $graph->renderer = new ezcGraphRenderer2d(); + * + * $graph->renderer->options->barMargin = .2; + * $graph->renderer->options->barPadding = .2; + * + * $graph->renderer->options->dataBorder = 0; + * + * $graph->render( 400, 150, 'tutorial_bar_chart_options.svg' ); + * + * + * For additional options, which are special to some chart type you may + * also want to check the option classes for the repective chart type you + * are using and the elements of the chart. The chart type dependant option + * classes are: + * + * - ezcGraphLineChartOptions + * - ezcGraphPieChartOptions + * - ezcGraphRadarChartOptions + * + * There may be additional options dependant on the renderer you are using. + * You may want to check the extensions of this class: + * + * - ezcGraphRenderer2dOptions + * - ezcGraphRenderer3dOptions + * + * @property float $maxLabelHeight + * Percent of chart height used as maximum height for pie chart + * labels. + * @property bool $showSymbol + * Indicates wheather to show the line between pie elements and + * labels. + * @property float $symbolSize + * Size of symbols used to connect a label with a pie. + * @property float $moveOut + * Percent to move pie chart elements out of the middle on highlight. + * @property int $titlePosition + * Position of title in a box. + * @property int $titleAlignement + * Alignement of box titles. + * @property float $dataBorder + * Factor to darken border of data elements, like lines, bars and + * pie segments. + * @property float $barMargin + * Percentual distance between bar blocks. + * @property float $barPadding + * Percentual distance between bars. + * @property float $pieChartOffset + * Offset for starting with first pie chart segment in degrees. + * @property float $legendSymbolGleam + * Opacity of gleam in legend symbols + * @property float $legendSymbolGleamSize + * Size of gleam in legend symbols + * @property float $legendSymbolGleamColor + * Color of gleam in legend symbols + * @property float $pieVerticalSize + * Percent of vertical space used for maximum pie chart size. + * @property float $pieHorizontalSize + * Percent of horizontal space used for maximum pie chart size. + * @property float $pieChartSymbolColor + * Color of pie chart symbols + * @property float $pieChartGleam + * Enhance pie chart with gleam on top. + * @property float $pieChartGleamColor + * Color used for gleam on pie charts. + * @property float $pieChartGleamBorder + * Do not draw gleam on an outer border of this size. + * @property bool $syncAxisFonts + * Synchronize fonts of axis. With the defaut true value, the only + * the fonts of the yAxis will be used. + * @property bool $axisEndStyle + * Style of axis end markers. Defauls to arrow heads, but you may + * also use all symbol constants defined ein the ezcGraph class, + * especially ezcGraph::NO_SYMBOL. + * @property bool $shortAxis + * Defines wheather to render the axis extending the chart boundings + * or stop them at the chart boundings. Deafults to false. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphRendererOptions extends ezcGraphChartOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['maxLabelHeight'] = .10; + $this->properties['showSymbol'] = true; + $this->properties['symbolSize'] = 6; + $this->properties['moveOut'] = .1; + $this->properties['titlePosition'] = ezcGraph::TOP; + $this->properties['titleAlignement'] = ezcGraph::MIDDLE | ezcGraph::CENTER; + $this->properties['dataBorder'] = .5; + $this->properties['barMargin'] = .1; + $this->properties['barPadding'] = .05; + $this->properties['pieChartOffset'] = 0; + $this->properties['pieChartSymbolColor'] = ezcGraphColor::fromHex( '#000000' ); + $this->properties['pieChartGleam'] = false; + $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); + $this->properties['pieChartGleamBorder'] = 0; + $this->properties['legendSymbolGleam'] = false; + $this->properties['legendSymbolGleamSize'] = .9; + $this->properties['legendSymbolGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); + $this->properties['pieVerticalSize'] = .5; + $this->properties['pieHorizontalSize'] = .25; + $this->properties['syncAxisFonts'] = true; + $this->properties['axisEndStyle'] = ezcGraph::ARROW; + $this->properties['shortAxis'] = false; + + parent::__construct( $options ); + } + + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'dataBorder': + case 'pieChartGleam': + case 'legendSymbolGleam': + if ( $propertyValue !== false && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' ); + } + + $this->properties[$propertyName] = ( + $propertyValue === false + ? false + : (float) $propertyValue ); + break; + + case 'maxLabelHeight': + case 'moveOut': + case 'barMargin': + case 'barPadding': + case 'legendSymbolGleamSize': + case 'pieVerticalSize': + case 'pieHorizontalSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'symbolSize': + case 'titlePosition': + case 'titleAlignement': + case 'pieChartGleamBorder': + case 'axisEndStyle': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' ); + } + + $this->properties[$propertyName] = (int) $propertyValue; + break; + + case 'showSymbol': + case 'syncAxisFonts': + case 'shortAxis': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + $this->properties[$propertyName] = (bool) $propertyValue; + break; + + case 'pieChartOffset': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 360 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 360' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'pieChartSymbolColor': + case 'pieChartGleamColor': + case 'legendSymbolGleamColor': + $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue ); + break; + + default: + return parent::__set( $propertyName, $propertyValue ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/renderer_2d.php b/library/ezc/Graph/src/options/renderer_2d.php index 1a553d001a..fde257407b 100644 --- a/library/ezc/Graph/src/options/renderer_2d.php +++ b/library/ezc/Graph/src/options/renderer_2d.php @@ -1,120 +1,120 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteBlack(); - * $graph->title = 'Access statistics'; - * $graph->options->label = '%2$d (%3$.1f%%)'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * $graph->data['Access statistics']->highlight['Explorer'] = true; - * - * // $graph->renderer = new ezcGraphRenderer2d(); - * - * $graph->renderer->options->moveOut = .2; - * - * $graph->renderer->options->pieChartOffset = 63; - * - * $graph->renderer->options->pieChartGleam = .3; - * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; - * $graph->renderer->options->pieChartGleamBorder = 2; - * - * $graph->renderer->options->pieChartShadowSize = 3; - * $graph->renderer->options->pieChartShadowColor = '#000000'; - * - * $graph->renderer->options->legendSymbolGleam = .5; - * $graph->renderer->options->legendSymbolGleamSize = .9; - * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartSymbolColor = '#BABDB688'; - * - * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' ); - * - * - * @property int $pieChartShadowSize - * Size of shadows. - * @property float $pieChartShadowTransparency - * Used transparency for pie chart shadows. - * @property float $pieChartShadowColor - * Color used for pie chart shadows. - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphRenderer2dOptions extends ezcGraphRendererOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['pieChartShadowSize'] = 0; - $this->properties['pieChartShadowTransparency'] = .3; - $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' ); - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'pieChartShadowSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= 0' ); - } - - $this->properties['pieChartShadowSize'] = (int) $propertyValue; - break; - case 'pieChartShadowTransparency': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['pieChartShadowTransparency'] = (float) $propertyValue; - break; - case 'pieChartShadowColor': - $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue ); - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteBlack(); + * $graph->title = 'Access statistics'; + * $graph->options->label = '%2$d (%3$.1f%%)'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * $graph->data['Access statistics']->highlight['Explorer'] = true; + * + * // $graph->renderer = new ezcGraphRenderer2d(); + * + * $graph->renderer->options->moveOut = .2; + * + * $graph->renderer->options->pieChartOffset = 63; + * + * $graph->renderer->options->pieChartGleam = .3; + * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; + * $graph->renderer->options->pieChartGleamBorder = 2; + * + * $graph->renderer->options->pieChartShadowSize = 3; + * $graph->renderer->options->pieChartShadowColor = '#000000'; + * + * $graph->renderer->options->legendSymbolGleam = .5; + * $graph->renderer->options->legendSymbolGleamSize = .9; + * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartSymbolColor = '#BABDB688'; + * + * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' ); + * + * + * @property int $pieChartShadowSize + * Size of shadows. + * @property float $pieChartShadowTransparency + * Used transparency for pie chart shadows. + * @property float $pieChartShadowColor + * Color used for pie chart shadows. + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphRenderer2dOptions extends ezcGraphRendererOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['pieChartShadowSize'] = 0; + $this->properties['pieChartShadowTransparency'] = .3; + $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' ); + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'pieChartShadowSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= 0' ); + } + + $this->properties['pieChartShadowSize'] = (int) $propertyValue; + break; + case 'pieChartShadowTransparency': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['pieChartShadowTransparency'] = (float) $propertyValue; + break; + case 'pieChartShadowColor': + $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue ); + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/renderer_3d.php b/library/ezc/Graph/src/options/renderer_3d.php index 2a906ff1c5..a5834ec2ce 100644 --- a/library/ezc/Graph/src/options/renderer_3d.php +++ b/library/ezc/Graph/src/options/renderer_3d.php @@ -1,189 +1,189 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzRed(); - * $graph->title = 'Access statistics'; - * $graph->options->label = '%2$d (%3$.1f%%)'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * $graph->data['Access statistics']->highlight['Explorer'] = true; - * - * $graph->renderer = new ezcGraphRenderer3d(); - * - * $graph->renderer->options->moveOut = .2; - * - * $graph->renderer->options->pieChartOffset = 63; - * - * $graph->renderer->options->pieChartGleam = .3; - * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartShadowSize = 5; - * $graph->renderer->options->pieChartShadowColor = '#000000'; - * - * $graph->renderer->options->legendSymbolGleam = .5; - * $graph->renderer->options->legendSymbolGleamSize = .9; - * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartSymbolColor = '#55575388'; - * - * $graph->renderer->options->pieChartHeight = 5; - * $graph->renderer->options->pieChartRotation = .8; - * - * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' ); - * - * - * @property bool $seperateLines - * Indicates wheather the full depth should be used for each line in - * the chart, or beeing seperated by the count of lines. - * @property float $fillAxis - * Transparency used to fill the axis polygon. - * @property float $fillGrid - * Transparency used to fill the grid lines. - * @property float $depth - * Part of picture used to simulate depth of three dimensional chart. - * @property float $pieChartHeight - * Height of the pie charts border. - * @property float $pieChartRotation - * Rotation of pie chart. Defines the percent of width used to - * calculate the height of the ellipse. - * @property int $pieChartShadowSize - * Size of shadows. - * @property float $pieChartShadowTransparency - * Used transparency for pie chart shadows. - * @property float $pieChartShadowColor - * Color used for pie chart shadows. - * @property float $barDarkenSide - * Factor to darken the color used for the bars side polygon. - * @property float $barDarkenTop - * Factor to darken the color used for the bars top polygon. - * @property float $barChartGleam - * Transparancy for gleam on bar charts - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphRenderer3dOptions extends ezcGraphRendererOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['seperateLines'] = true; - $this->properties['fillAxis'] = .8; - $this->properties['fillGrid'] = 0; - $this->properties['depth'] = .1; - $this->properties['pieChartHeight'] = 10.; - $this->properties['pieChartRotation'] = .6; - $this->properties['pieChartShadowSize'] = 0; - $this->properties['pieChartShadowTransparency'] = .3; - $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' ); - $this->properties['pieChartGleam'] = false; - $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); - $this->properties['barDarkenSide'] = .2; - $this->properties['barDarkenTop'] = .4; - $this->properties['barChartGleam'] = false; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'fillAxis': - case 'fillGrid': - if ( $propertyValue !== false && - !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' ); - } - - $this->properties[$propertyName] = ( - $propertyValue === false - ? false - : (float) $propertyValue ); - break; - - case 'depth': - case 'pieChartRotation': - case 'pieChartShadowTransparency': - case 'barDarkenSide': - case 'barDarkenTop': - case 'barChartGleam': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'pieChartHeight': - case 'pieChartShadowSize': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue <= 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties[$propertyName] = (float) $propertyValue; - break; - - case 'seperateLines': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['seperateLines'] = $propertyValue; - break; - case 'pieChartShadowColor': - $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue ); - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzRed(); + * $graph->title = 'Access statistics'; + * $graph->options->label = '%2$d (%3$.1f%%)'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * $graph->data['Access statistics']->highlight['Explorer'] = true; + * + * $graph->renderer = new ezcGraphRenderer3d(); + * + * $graph->renderer->options->moveOut = .2; + * + * $graph->renderer->options->pieChartOffset = 63; + * + * $graph->renderer->options->pieChartGleam = .3; + * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartShadowSize = 5; + * $graph->renderer->options->pieChartShadowColor = '#000000'; + * + * $graph->renderer->options->legendSymbolGleam = .5; + * $graph->renderer->options->legendSymbolGleamSize = .9; + * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartSymbolColor = '#55575388'; + * + * $graph->renderer->options->pieChartHeight = 5; + * $graph->renderer->options->pieChartRotation = .8; + * + * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' ); + * + * + * @property bool $seperateLines + * Indicates wheather the full depth should be used for each line in + * the chart, or beeing seperated by the count of lines. + * @property float $fillAxis + * Transparency used to fill the axis polygon. + * @property float $fillGrid + * Transparency used to fill the grid lines. + * @property float $depth + * Part of picture used to simulate depth of three dimensional chart. + * @property float $pieChartHeight + * Height of the pie charts border. + * @property float $pieChartRotation + * Rotation of pie chart. Defines the percent of width used to + * calculate the height of the ellipse. + * @property int $pieChartShadowSize + * Size of shadows. + * @property float $pieChartShadowTransparency + * Used transparency for pie chart shadows. + * @property float $pieChartShadowColor + * Color used for pie chart shadows. + * @property float $barDarkenSide + * Factor to darken the color used for the bars side polygon. + * @property float $barDarkenTop + * Factor to darken the color used for the bars top polygon. + * @property float $barChartGleam + * Transparancy for gleam on bar charts + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphRenderer3dOptions extends ezcGraphRendererOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['seperateLines'] = true; + $this->properties['fillAxis'] = .8; + $this->properties['fillGrid'] = 0; + $this->properties['depth'] = .1; + $this->properties['pieChartHeight'] = 10.; + $this->properties['pieChartRotation'] = .6; + $this->properties['pieChartShadowSize'] = 0; + $this->properties['pieChartShadowTransparency'] = .3; + $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' ); + $this->properties['pieChartGleam'] = false; + $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' ); + $this->properties['barDarkenSide'] = .2; + $this->properties['barDarkenTop'] = .4; + $this->properties['barChartGleam'] = false; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'fillAxis': + case 'fillGrid': + if ( $propertyValue !== false && + !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' ); + } + + $this->properties[$propertyName] = ( + $propertyValue === false + ? false + : (float) $propertyValue ); + break; + + case 'depth': + case 'pieChartRotation': + case 'pieChartShadowTransparency': + case 'barDarkenSide': + case 'barDarkenTop': + case 'barChartGleam': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'pieChartHeight': + case 'pieChartShadowSize': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue <= 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties[$propertyName] = (float) $propertyValue; + break; + + case 'seperateLines': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['seperateLines'] = $propertyValue; + break; + case 'pieChartShadowColor': + $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue ); + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } +} + +?> diff --git a/library/ezc/Graph/src/options/svg_driver.php b/library/ezc/Graph/src/options/svg_driver.php index 8eb46a182c..45e9a0c87a 100644 --- a/library/ezc/Graph/src/options/svg_driver.php +++ b/library/ezc/Graph/src/options/svg_driver.php @@ -1,272 +1,272 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->background->color = '#FFFFFFFF'; - * $graph->title = 'Access statistics'; - * $graph->legend = false; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * - * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg'; - * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 ); - * $graph->driver->options->insertIntoGroup = 'ezcGraph'; - * - * $graph->render( 400, 200, 'tutorial_driver_svg.svg' ); - * - * - * @property string $encoding - * Encoding of the SVG XML document - * @property float $assumedNumericCharacterWidth - * Assumed percentual average width of chars in numeric strings with - * the used font. - * @property float $assumedTextCharacterWidth - * Assumed percentual average width of chars in non numeric strings - * with the used font. - * @property string $strokeLineCap - * This specifies the shape to be used at the end of open subpaths - * when they are stroked. - * @property string $strokeLineJoin - * This specifies the shape to be used at the edges of paths. - * @property string $shapeRendering - * "The creator of SVG content might want to provide a hint to the - * implementation about what tradeoffs to make as it renders vector - * graphics elements such as 'path' elements and basic shapes such as - * circles and rectangles." - * @property string $colorRendering - * "The creator of SVG content might want to provide a hint to the - * implementation about how to make speed vs. quality tradeoffs as it - * performs color interpolation and compositing. The - * 'color-rendering' property provides a hint to the SVG user agent - * about how to optimize its color interpolation and compositing - * operations." - * @property string $textRendering - * "The creator of SVG content might want to provide a hint to the - * implementation about what tradeoffs to make as it renders text." - * @property mixed $templateDocument - * Use existing SVG document as template to insert graph into. If - * insertIntoGroup is not set, a new group will be inserted in the - * svg root node. - * @property mixed $insertIntoGroup - * ID of a SVG group node to insert the graph. Only works with a - * custom template document. - * @property ezcGraphCoordinate $graphOffset - * Offset of the graph in the svg. - * @property string $idPrefix - * Prefix used for the ids in SVG documents. - * @property string $linkCursor - * CSS value for cursor property used for linked SVG elements - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphSvgDriverOptions extends ezcGraphDriverOptions -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['encoding'] = null; - $this->properties['assumedNumericCharacterWidth'] = .62; - $this->properties['assumedTextCharacterWidth'] = .53; - $this->properties['strokeLineJoin'] = 'round'; - $this->properties['strokeLineCap'] = 'round'; - $this->properties['shapeRendering'] = 'geometricPrecision'; - $this->properties['colorRendering'] = 'optimizeQuality'; - $this->properties['textRendering'] = 'optimizeLegibility'; - $this->properties['templateDocument'] = false; - $this->properties['insertIntoGroup'] = false; - $this->properties['graphOffset'] = new ezcGraphCoordinate( 0, 0 ); - $this->properties['idPrefix'] = 'ezcGraph'; - $this->properties['linkCursor'] = 'pointer'; - - parent::__construct( $options ); - } - - /** - * Set an option value - * - * @param string $propertyName - * @param mixed $propertyValue - * @throws ezcBasePropertyNotFoundException - * If a property is not defined in this class - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'assumedNumericCharacterWidth': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['assumedNumericCharacterWidth'] = (float) $propertyValue; - break; - case 'assumedTextCharacterWidth': - if ( !is_numeric( $propertyValue ) || - ( $propertyValue < 0 ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); - } - - $this->properties['assumedTextCharacterWidth'] = (float) $propertyValue; - break; - case 'strokeLineJoin': - $values = array( - 'round', - 'miter', - 'bevel', - 'inherit', - ); - - if ( in_array( $propertyValue, $values, true ) ) - { - $this->properties['strokeLineJoin'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); - } - break; - case 'strokeLineCap': - $values = array( - 'round', - 'butt', - 'square', - 'inherit', - ); - - if ( in_array( $propertyValue, $values, true ) ) - { - $this->properties['strokeLineCap'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); - } - break; - case 'shapeRendering': - $values = array( - 'auto', - 'optimizeSpeed', - 'crispEdges', - 'geometricPrecision', - 'inherit', - ); - - if ( in_array( $propertyValue, $values, true ) ) - { - $this->properties['shapeRendering'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); - } - break; - case 'colorRendering': - $values = array( - 'auto', - 'optimizeSpeed', - 'optimizeQuality', - 'inherit', - ); - - if ( in_array( $propertyValue, $values, true ) ) - { - $this->properties['colorRendering'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); - } - break; - case 'textRendering': - $values = array( - 'auto', - 'optimizeSpeed', - 'optimizeLegibility', - 'geometricPrecision', - 'inherit', - ); - - if ( in_array( $propertyValue, $values, true ) ) - { - $this->properties['textRendering'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); - } - break; - case 'templateDocument': - if ( !is_file( $propertyValue ) || !is_readable( $propertyValue ) ) - { - throw new ezcBaseFileNotFoundException( $propertyValue ); - } - else - { - $this->properties['templateDocument'] = realpath( $propertyValue ); - } - break; - case 'insertIntoGroup': - if ( !is_string( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' ); - } - else - { - $this->properties['insertIntoGroup'] = $propertyValue; - } - break; - case 'graphOffset': - if ( $propertyValue instanceof ezcGraphCoordinate ) - { - $this->properties['graphOffset'] = $propertyValue; - } - else - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); - } - break; - case 'idPrefix': - $this->properties['idPrefix'] = (string) $propertyValue; - break; - case 'encoding': - $this->properties['encoding'] = (string) $propertyValue; - break; - case 'linkCursor': - $this->properties['linkCursor'] = (string) $propertyValue; - break; - default: - parent::__set( $propertyName, $propertyValue ); - break; - } - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->background->color = '#FFFFFFFF'; + * $graph->title = 'Access statistics'; + * $graph->legend = false; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * + * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg'; + * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 ); + * $graph->driver->options->insertIntoGroup = 'ezcGraph'; + * + * $graph->render( 400, 200, 'tutorial_driver_svg.svg' ); + * + * + * @property string $encoding + * Encoding of the SVG XML document + * @property float $assumedNumericCharacterWidth + * Assumed percentual average width of chars in numeric strings with + * the used font. + * @property float $assumedTextCharacterWidth + * Assumed percentual average width of chars in non numeric strings + * with the used font. + * @property string $strokeLineCap + * This specifies the shape to be used at the end of open subpaths + * when they are stroked. + * @property string $strokeLineJoin + * This specifies the shape to be used at the edges of paths. + * @property string $shapeRendering + * "The creator of SVG content might want to provide a hint to the + * implementation about what tradeoffs to make as it renders vector + * graphics elements such as 'path' elements and basic shapes such as + * circles and rectangles." + * @property string $colorRendering + * "The creator of SVG content might want to provide a hint to the + * implementation about how to make speed vs. quality tradeoffs as it + * performs color interpolation and compositing. The + * 'color-rendering' property provides a hint to the SVG user agent + * about how to optimize its color interpolation and compositing + * operations." + * @property string $textRendering + * "The creator of SVG content might want to provide a hint to the + * implementation about what tradeoffs to make as it renders text." + * @property mixed $templateDocument + * Use existing SVG document as template to insert graph into. If + * insertIntoGroup is not set, a new group will be inserted in the + * svg root node. + * @property mixed $insertIntoGroup + * ID of a SVG group node to insert the graph. Only works with a + * custom template document. + * @property ezcGraphCoordinate $graphOffset + * Offset of the graph in the svg. + * @property string $idPrefix + * Prefix used for the ids in SVG documents. + * @property string $linkCursor + * CSS value for cursor property used for linked SVG elements + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphSvgDriverOptions extends ezcGraphDriverOptions +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['encoding'] = null; + $this->properties['assumedNumericCharacterWidth'] = .62; + $this->properties['assumedTextCharacterWidth'] = .53; + $this->properties['strokeLineJoin'] = 'round'; + $this->properties['strokeLineCap'] = 'round'; + $this->properties['shapeRendering'] = 'geometricPrecision'; + $this->properties['colorRendering'] = 'optimizeQuality'; + $this->properties['textRendering'] = 'optimizeLegibility'; + $this->properties['templateDocument'] = false; + $this->properties['insertIntoGroup'] = false; + $this->properties['graphOffset'] = new ezcGraphCoordinate( 0, 0 ); + $this->properties['idPrefix'] = 'ezcGraph'; + $this->properties['linkCursor'] = 'pointer'; + + parent::__construct( $options ); + } + + /** + * Set an option value + * + * @param string $propertyName + * @param mixed $propertyValue + * @throws ezcBasePropertyNotFoundException + * If a property is not defined in this class + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'assumedNumericCharacterWidth': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['assumedNumericCharacterWidth'] = (float) $propertyValue; + break; + case 'assumedTextCharacterWidth': + if ( !is_numeric( $propertyValue ) || + ( $propertyValue < 0 ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' ); + } + + $this->properties['assumedTextCharacterWidth'] = (float) $propertyValue; + break; + case 'strokeLineJoin': + $values = array( + 'round', + 'miter', + 'bevel', + 'inherit', + ); + + if ( in_array( $propertyValue, $values, true ) ) + { + $this->properties['strokeLineJoin'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); + } + break; + case 'strokeLineCap': + $values = array( + 'round', + 'butt', + 'square', + 'inherit', + ); + + if ( in_array( $propertyValue, $values, true ) ) + { + $this->properties['strokeLineCap'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); + } + break; + case 'shapeRendering': + $values = array( + 'auto', + 'optimizeSpeed', + 'crispEdges', + 'geometricPrecision', + 'inherit', + ); + + if ( in_array( $propertyValue, $values, true ) ) + { + $this->properties['shapeRendering'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); + } + break; + case 'colorRendering': + $values = array( + 'auto', + 'optimizeSpeed', + 'optimizeQuality', + 'inherit', + ); + + if ( in_array( $propertyValue, $values, true ) ) + { + $this->properties['colorRendering'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); + } + break; + case 'textRendering': + $values = array( + 'auto', + 'optimizeSpeed', + 'optimizeLegibility', + 'geometricPrecision', + 'inherit', + ); + + if ( in_array( $propertyValue, $values, true ) ) + { + $this->properties['textRendering'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) ); + } + break; + case 'templateDocument': + if ( !is_file( $propertyValue ) || !is_readable( $propertyValue ) ) + { + throw new ezcBaseFileNotFoundException( $propertyValue ); + } + else + { + $this->properties['templateDocument'] = realpath( $propertyValue ); + } + break; + case 'insertIntoGroup': + if ( !is_string( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' ); + } + else + { + $this->properties['insertIntoGroup'] = $propertyValue; + } + break; + case 'graphOffset': + if ( $propertyValue instanceof ezcGraphCoordinate ) + { + $this->properties['graphOffset'] = $propertyValue; + } + else + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + break; + case 'idPrefix': + $this->properties['idPrefix'] = (string) $propertyValue; + break; + case 'encoding': + $this->properties['encoding'] = (string) $propertyValue; + break; + case 'linkCursor': + $this->properties['linkCursor'] = (string) $propertyValue; + break; + default: + parent::__set( $propertyName, $propertyValue ); + break; + } + } +} + +?> diff --git a/library/ezc/Graph/src/palette/black.php b/library/ezc/Graph/src/palette/black.php index 61b1b7cc42..86f7fc32dc 100644 --- a/library/ezc/Graph/src/palette/black.php +++ b/library/ezc/Graph/src/palette/black.php @@ -1,114 +1,114 @@ - + diff --git a/library/ezc/Graph/src/palette/ez.php b/library/ezc/Graph/src/palette/ez.php index 417caab83e..2d31a3833a 100644 --- a/library/ezc/Graph/src/palette/ez.php +++ b/library/ezc/Graph/src/palette/ez.php @@ -1,97 +1,97 @@ - + diff --git a/library/ezc/Graph/src/palette/ez_blue.php b/library/ezc/Graph/src/palette/ez_blue.php index 1b5f13dd95..ada89f7f33 100644 --- a/library/ezc/Graph/src/palette/ez_blue.php +++ b/library/ezc/Graph/src/palette/ez_blue.php @@ -1,90 +1,90 @@ - + diff --git a/library/ezc/Graph/src/palette/ez_green.php b/library/ezc/Graph/src/palette/ez_green.php index bc90d099f8..16bd6ef6ff 100644 --- a/library/ezc/Graph/src/palette/ez_green.php +++ b/library/ezc/Graph/src/palette/ez_green.php @@ -1,90 +1,90 @@ - + diff --git a/library/ezc/Graph/src/palette/ez_red.php b/library/ezc/Graph/src/palette/ez_red.php index ba8d00e9ae..4d825a097a 100644 --- a/library/ezc/Graph/src/palette/ez_red.php +++ b/library/ezc/Graph/src/palette/ez_red.php @@ -1,90 +1,90 @@ - + diff --git a/library/ezc/Graph/src/palette/tango.php b/library/ezc/Graph/src/palette/tango.php index a0b6e6b91d..e13f802b7a 100644 --- a/library/ezc/Graph/src/palette/tango.php +++ b/library/ezc/Graph/src/palette/tango.php @@ -1,87 +1,87 @@ - + diff --git a/library/ezc/Graph/src/renderer/2d.php b/library/ezc/Graph/src/renderer/2d.php index ed51bf3f08..e1b0951280 100644 --- a/library/ezc/Graph/src/renderer/2d.php +++ b/library/ezc/Graph/src/renderer/2d.php @@ -1,1885 +1,1885 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteBlack(); - * $graph->title = 'Access statistics'; - * $graph->options->label = '%2$d (%3$.1f%%)'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * $graph->data['Access statistics']->highlight['Explorer'] = true; - * - * // $graph->renderer = new ezcGraphRenderer2d(); - * - * $graph->renderer->options->moveOut = .2; - * - * $graph->renderer->options->pieChartOffset = 63; - * - * $graph->renderer->options->pieChartGleam = .3; - * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; - * $graph->renderer->options->pieChartGleamBorder = 2; - * - * $graph->renderer->options->pieChartShadowSize = 3; - * $graph->renderer->options->pieChartShadowColor = '#000000'; - * - * $graph->renderer->options->legendSymbolGleam = .5; - * $graph->renderer->options->legendSymbolGleamSize = .9; - * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartSymbolColor = '#BABDB688'; - * - * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' ); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphRenderer2d - extends - ezcGraphRenderer - implements - ezcGraphRadarRenderer, ezcGraphStackedBarsRenderer, ezcGraphOdometerRenderer -{ - - /** - * Pie segment labels divided into two array, containing the labels on the - * left and right side of the pie chart center. - * - * @var array - */ - protected $pieSegmentLabels = array( - 0 => array(), - 1 => array(), - ); - - /** - * Contains the boundings used for pie segments - * - * @var ezcGraphBoundings - */ - protected $pieSegmentBoundings = false; - - /** - * Array with symbols for post processing, which ensures, that the symbols - * are rendered topmost. - * - * @var array - */ - protected $linePostSymbols = array(); - - /** - * Options - * - * @var ezcGraphRenderer2dOptions - */ - protected $options; - - /** - * Collect axis labels, so that the axis are drawn, when all axis spaces - * are known. - * - * @var array - */ - protected $axisLabels = array(); - - /** - * Collects circle sectors to draw shadow in background of all circle - * sectors. - * - * @var array - */ - protected $circleSectors = array(); - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->options = new ezcGraphRenderer2dOptions( $options ); - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'options': - return $this->options; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Draw pie segment - * - * Draws a single pie segment - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of pie segment - * @param float $startAngle Start angle - * @param float $endAngle End angle - * @param mixed $label Label of pie segment - * @param bool $moveOut Move out from middle for hilighting - * @return void - */ - public function drawPieSegment( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - $startAngle = .0, - $endAngle = 360., - $label = false, - $moveOut = false ) - { - // Apply offset - $startAngle += $this->options->pieChartOffset; - $endAngle += $this->options->pieChartOffset; - - // Calculate position and size of pie - $center = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) / 2, - $boundings->y0 + ( $boundings->height ) / 2 - ); - - // Limit radius to fourth of width and half of height at maximum - $radius = min( - ( $boundings->width ) * $this->options->pieHorizontalSize, - ( $boundings->height ) * $this->options->pieVerticalSize - ); - - // Move pie segment out of the center - if ( $moveOut ) - { - $direction = ( $endAngle + $startAngle ) / 2; - - $center = new ezcGraphCoordinate( - $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ), - $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) - ); - } - - // Add circle sector to queue - $this->circleSectors[] = array( - 'center' => $center, - 'context' => $context, - 'width' => $radius * 2 * ( 1 - $this->options->moveOut ), - 'height' => $radius * 2 * ( 1 - $this->options->moveOut ), - 'start' => $startAngle, - 'end' => $endAngle, - 'color' => $color, - ); - - if ( $label ) - { - // Determine position of label - $direction = ( $endAngle + $startAngle ) / 2; - $pieSegmentCenter = new ezcGraphCoordinate( - $center->x + cos( deg2rad( $direction ) ) * $radius, - $center->y + sin( deg2rad( $direction ) ) * $radius - ); - - // Split labels up into left an right size and index them on their - // y position - $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array( - new ezcGraphCoordinate( - $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3, - $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3 - ), - $label, - $context - ); - } - - if ( !$this->pieSegmentBoundings ) - { - $this->pieSegmentBoundings = $boundings; - } - } - - /** - * Draws the collected circle sectors - * - * All circle sectors are collected and drawn later to be able to render - * the shadows of the pie segments in the back of all pie segments. - * - * @return void - */ - protected function finishCircleSectors() - { - // Add circle sector sides to simple z buffer prioriry list - if ( $this->options->pieChartShadowSize > 0 ) - { - foreach ( $this->circleSectors as $circleSector ) - { - $this->driver->drawCircleSector( - new ezcGraphCoordinate( - $circleSector['center']->x + $this->options->pieChartShadowSize, - $circleSector['center']->y + $this->options->pieChartShadowSize - ), - $circleSector['width'], - $circleSector['height'], - $circleSector['start'], - $circleSector['end'], - $this->options->pieChartShadowColor->transparent( $this->options->pieChartShadowTransparency ), - true - ); - } - } - - foreach ( $this->circleSectors as $circleSector ) - { - // Draw circle sector - $this->addElementReference( - $circleSector['context'], - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - $circleSector['start'], - $circleSector['end'], - $circleSector['color'], - true - ) - ); - - $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - $circleSector['start'], - $circleSector['end'], - $darkenedColor, - false - ); - - if ( $this->options->pieChartGleam !== false ) - { - $gradient = new ezcGraphLinearGradient( - $circleSector['center'], - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y - $circleSector['height'] / 2 - ), - $this->options->pieChartGleamColor->transparent( 1 ), - $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) - ); - - $this->addElementReference( $circleSector['context'], - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'] - $this->options->pieChartGleamBorder * 2, - $circleSector['height'] - $this->options->pieChartGleamBorder * 2, - $circleSector['start'], - $circleSector['end'], - $gradient, - true - ) - ); - - $gradient = new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $circleSector['height'] / 4 - ), - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $circleSector['height'] / 2 - ), - $this->options->pieChartGleamColor->transparent( 1 ), - $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) - ); - - $this->addElementReference( $circleSector['context'], - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'] - $this->options->pieChartGleamBorder * 2, - $circleSector['height'] - $this->options->pieChartGleamBorder * 2, - $circleSector['start'], - $circleSector['end'], - $gradient, - true - ) - ); - } - } - } - - /** - * Draws the collected pie segment labels - * - * All labels are collected and drawn later to be able to partition the - * available space for the labels woth knowledge of the overall label - * count and their required size and optimal position. - * - * @return void - */ - protected function finishPieSegmentLabels() - { - if ( $this->pieSegmentBoundings === false ) - { - return true; - } - - $boundings = $this->pieSegmentBoundings; - - // Calculate position and size of pie - $center = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) / 2, - $boundings->y0 + ( $boundings->height ) / 2 - ); - - // Limit radius to fourth of width and half of height at maximum - $radius = min( - ( $boundings->width ) * $this->options->pieHorizontalSize, - ( $boundings->height ) * $this->options->pieVerticalSize - ); - - $pieChartHeight = min( - $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4, - $boundings->height - ); - $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2; - - // Calculate maximum height of labels - $labelHeight = min( - ( count( $this->pieSegmentLabels[0] ) - ? $pieChartHeight / count( $this->pieSegmentLabels[0] ) - : $pieChartHeight - ), - ( count( $this->pieSegmentLabels[1] ) - ? $pieChartHeight / count( $this->pieSegmentLabels[1] ) - : $pieChartHeight - ), - ( $pieChartHeight ) * $this->options->maxLabelHeight - ); - - $symbolSize = $this->options->symbolSize; - - foreach ( $this->pieSegmentLabels as $side => $labelPart ) - { - $minHeight = $pieChartYPosition; - $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight; - - // Sort to draw topmost label first - ksort( $labelPart ); - $sign = ( $side ? -1 : 1 ); - - foreach ( $labelPart as $height => $label ) - { - if ( ( $height - $labelHeight / 2 ) > $minHeight ) - { - $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight ); - $minHeight += $share; - $toShare -= $share; - } - - // Determine position of label - $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare; - $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius; - - $labelPosition = new ezcGraphCoordinate( - $center->x - - $sign * ( - abs( $verticalDistance ) > 1 - // If vertical distance to center is greater then the - // radius, use the centerline for the horizontal - // position - ? max ( - 5, - abs( $label[0]->x - $center->x ) - ) - // Else place the label outside of the pie chart - : ( cos ( asin ( $verticalDistance ) ) * $radius + - $symbolSize * (int) $this->options->showSymbol - ) - ), - $minHeight + $labelHeight / 2 - ); - - if ( $this->options->showSymbol ) - { - // Draw label - $this->driver->drawLine( - $label[0], - $labelPosition, - $this->options->pieChartSymbolColor, - 1 - ); - - $this->driver->drawCircle( - $label[0], - $symbolSize, - $symbolSize, - $this->options->pieChartSymbolColor, - true - ); - $this->driver->drawCircle( - $labelPosition, - $symbolSize, - $symbolSize, - $this->options->pieChartSymbolColor, - true - ); - } - - $this->addElementReference( - $label[2], - $this->driver->drawTextBox( - $label[1], - new ezcGraphCoordinate( - ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ), - $minHeight - ), - ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ), - $labelHeight, - ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE - ) - ); - - // Add used space to minHeight - $minHeight += $labelHeight; - } - } - } - - /** - * Draw the collected line symbols - * - * Symbols for the data lines are collected and delayed to ensure that - * they are not covered and hidden by other data lines. - * - * @return void - */ - protected function finishLineSymbols() - { - foreach ( $this->linePostSymbols as $symbol ) - { - $this->addElementReference( - $symbol['context'], - $this->drawSymbol( - $symbol['boundings'], - $symbol['color'], - $symbol['symbol'] - ) - ); - } - } - - /** - * Draw bar - * - * Draws a bar as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $position Position of data point - * @param float $stepSize Space which can be used for bars - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param float $axisPosition Position of axis for drawing filled lines - * @return void - */ - public function drawBar( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $stepSize, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - $axisPosition = 0. ) - { - // Apply margin - $margin = $stepSize * $this->options->barMargin; - $padding = $stepSize * $this->options->barPadding; - $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; - $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2; - - $barPointArray = array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset, - $boundings->y0 + ( $boundings->height ) * $position->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, - $boundings->y0 + ( $boundings->height ) * $position->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - ); - - $this->addElementReference( - $context, - $this->driver->drawPolygon( - $barPointArray, - $color, - true - ) - ); - - if ( $this->options->dataBorder > 0 ) - { - $darkened = $color->darken( $this->options->dataBorder ); - $this->driver->drawPolygon( - $barPointArray, - $darkened, - false, - 1 - ); - } - } - - /** - * Draw stacked bar - * - * Draws a stacked bar part as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $start - * @param ezcGraphCoordinate $position - * @param float $stepSize Space which can be used for bars - * @param int $symbol Symbol to draw for line - * @param float $axisPosition Position of axis for drawing filled lines - * @return void - */ - public function drawStackedBar( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $start, - ezcGraphCoordinate $position, - $stepSize, - $symbol = ezcGraph::NO_SYMBOL, - $axisPosition = 0. ) - { - // Apply margin - $margin = $stepSize * $this->options->barMargin; - $barWidth = $stepSize - $margin; - $offset = - $stepSize / 2 + $margin / 2; - - $barPointArray = array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset, - $boundings->y0 + ( $boundings->height ) * $start->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset, - $boundings->y0 + ( $boundings->height ) * $position->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, - $boundings->y0 + ( $boundings->height ) * $position->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, - $boundings->y0 + ( $boundings->height ) * $start->y - ), - ); - - $this->addElementReference( - $context, - $this->driver->drawPolygon( - $barPointArray, - $color, - true - ) - ); - - if ( $this->options->dataBorder > 0 ) - { - $darkened = $color->darken( $this->options->dataBorder ); - $this->driver->drawPolygon( - $barPointArray, - $darkened, - false, - 1 - ); - } - } - - /** - * Draw data line - * - * Draws a line as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $start Starting point - * @param ezcGraphCoordinate $end Ending point - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor - * @param ezcGraphColor $fillColor Color to fill line with - * @param float $axisPosition Position of axis for drawing filled lines - * @param float $thickness Line thickness - * @return void - */ - public function drawDataLine( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - ezcGraphColor $symbolColor = null, - ezcGraphColor $fillColor = null, - $axisPosition = 0., - $thickness = 1. ) - { - // Perhaps fill up line - if ( $fillColor !== null && - $start->x != $end->x ) - { - $startValue = $axisPosition - $start->y; - $endValue = $axisPosition - $end->y; - - if ( ( $startValue == 0 ) || - ( $endValue == 0 ) || - ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) ) - { - // Values have the same sign or are on the axis - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $start->x, - $boundings->y0 + ( $boundings->height ) * $start->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x, - $boundings->y0 + ( $boundings->height ) * $end->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $start->x, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - ), - $fillColor, - true - ); - } - else - { - // values are on differente sides of the axis - split the filled polygon - $startDiff = abs( $axisPosition - $start->y ); - $endDiff = abs( $axisPosition - $end->y ); - - $cuttingPosition = $startDiff / ( $endDiff + $startDiff ); - $cuttingPoint = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $cuttingPosition, - $axisPosition - ); - - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $start->x, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $start->x, - $boundings->y0 + ( $boundings->height ) * $start->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x, - $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y - ), - ), - $fillColor, - true - ); - - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x, - $boundings->y0 + ( $boundings->height ) * $axisPosition - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x, - $boundings->y0 + ( $boundings->height ) * $end->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x, - $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y - ), - ), - $fillColor, - true - ); - } - } - - // Draw line - $this->driver->drawLine( - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $start->x, - $boundings->y0 + ( $boundings->height ) * $start->y - ), - new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x, - $boundings->y0 + ( $boundings->height ) * $end->y - ), - $color, - $thickness - ); - - // Draw line symbol - if ( $symbol !== ezcGraph::NO_SYMBOL ) - { - if ( $symbolColor === null ) - { - $symbolColor = $color; - } - - $this->linePostSymbols[] = array( - 'boundings' => new ezcGraphBoundings( - $boundings->x0 + ( $boundings->width ) * $end->x - $this->options->symbolSize / 2, - $boundings->y0 + ( $boundings->height ) * $end->y - $this->options->symbolSize / 2, - $boundings->x0 + ( $boundings->width ) * $end->x + $this->options->symbolSize / 2, - $boundings->y0 + ( $boundings->height ) * $end->y + $this->options->symbolSize / 2 - ), - 'color' => $symbolColor, - 'context' => $context, - 'symbol' => $symbol, - ); - } - } - - /** - * Returns a coordinate in the given bounding box for the given angle - * radius with the center as base point. - * - * @param ezcGraphBoundings $boundings - * @param ezcGraphCoordinate $center - * @param float $angle - * @param float $radius - * @return float - */ - protected function getCoordinateFromAngleAndRadius( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $center, - $angle, - $radius - ) - { - $direction = new ezcGraphCoordinate( - sin( $angle ) * $boundings->width / 2, - -cos( $angle ) * $boundings->height / 2 - ); - - $offset = new ezcGraphCoordinate( - sin( $angle ) * $this->xAxisSpace, - -cos( $angle ) * $this->yAxisSpace - ); - - $direction->x -= 2 * $offset->x; - $direction->y -= 2 * $offset->y; - - return new ezcGraphCoordinate( - $boundings->x0 + - $center->x + - $offset->x + - $direction->x * $radius, - $boundings->y0 + - $center->y + - $offset->y + - $direction->y * $radius - ); - } - - /** - * Draw radar chart data line - * - * Draws a line as a data element in a radar chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $center Center of radar chart - * @param ezcGraphCoordinate $start Starting point - * @param ezcGraphCoordinate $end Ending point - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor - * @param ezcGraphColor $fillColor Color to fill line with - * @param float $thickness Line thickness - * @return void - */ - public function drawRadarDataLine( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $center, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - ezcGraphColor $symbolColor = null, - ezcGraphColor $fillColor = null, - $thickness = 1. - ) - { - // Calculate line points from chart coordinates - $start = $this->getCoordinateFromAngleAndRadius( - $boundings, - $center, - $start->x * 2 * M_PI, - $start->y - ); - $end = $this->getCoordinateFromAngleAndRadius( - $boundings, - $center, - $end->x * 2 * M_PI, - $end->y - ); - - // Fill line - if ( $fillColor !== null ) - { - $this->driver->drawPolygon( - array( - $start, - $end, - new ezcGraphCoordinate( - $boundings->x0 + $center->x, - $boundings->y0 + $center->y - ), - ), - $fillColor, - true - ); - } - - // Draw line - $this->driver->drawLine( - $start, - $end, - $color, - $thickness - ); - - if ( $symbol !== ezcGraph::NO_SYMBOL ) - { - $this->drawSymbol( - new ezcGraphBoundings( - $end->x - $this->options->symbolSize / 2, - $end->y - $this->options->symbolSize / 2, - $end->x + $this->options->symbolSize / 2, - $end->y + $this->options->symbolSize / 2 - ), - $symbolColor, - $symbol - ); - } - } - - /** - * Draws a highlight textbox for a datapoint. - * - * A highlight textbox for line and bar charts means a box with the current - * value in the graph. - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphCoordinate $end Ending point - * @param float $axisPosition Position of axis for drawing filled lines - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param ezcGraphFontOptions $font Font used for highlight string - * @param string $text Acutual value - * @param int $size Size of highlight text - * @param ezcGraphColor $markLines - * @param int $xOffset - * @param int $yOffset - * @param float $stepSize - * @param int $type - * @return void - */ - public function drawDataHighlightText( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphCoordinate $end, - $axisPosition = 0., - $dataNumber = 1, - $dataCount = 1, - ezcGraphFontOptions $font, - $text, - $size, - ezcGraphColor $markLines = null, - $xOffset = 0, - $yOffset = 0, - $stepSize = 0., - $type = ezcGraph::LINE ) - { - // Bar specific calculations - if ( $type !== ezcGraph::LINE ) - { - $margin = $stepSize * $this->options->barMargin; - $padding = $stepSize * $this->options->barPadding; - $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; - $offset = -( $dataNumber + ( $dataCount - 1 ) / -2 ) * ( $barWidth + $padding ); - } - - $this->driver->options->font = $font; - $width = $boundings->width / $dataCount; - - $dataPoint = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->width ) * $end->x + $xOffset + - ( $type === ezcGraph::LINE ? 0 : $offset ), - $boundings->y0 + ( $boundings->height ) * $end->y + $yOffset - ); - - if ( $end->y < $axisPosition ) - { - $this->driver->drawTextBox( - $text, - new ezcGraphCoordinate( - $dataPoint->x - $width / 2, - $dataPoint->y - $size - $font->padding - $this->options->symbolSize - ), - $width, - $size, - ezcGraph::CENTER | ezcGraph::BOTTOM - ); - } - else - { - $this->driver->drawTextBox( - $text, - new ezcGraphCoordinate( - $dataPoint->x - $width / 2, - $dataPoint->y + $font->padding + $this->options->symbolSize - ), - $width, - $size, - ezcGraph::CENTER | ezcGraph::TOP - ); - } - } - - /** - * Draw legend - * - * Will draw a legend in the bounding box - * - * @param ezcGraphBoundings $boundings Bounding of legend - * @param ezcGraphChartElementLegend $legend Legend to draw; - * @param int $type Type of legend: Protrait or landscape - * @return void - */ - public function drawLegend( - ezcGraphBoundings $boundings, - ezcGraphChartElementLegend $legend, - $type = ezcGraph::VERTICAL ) - { - $labels = $legend->labels; - - // Calculate boundings of each label - if ( $type & ezcGraph::VERTICAL ) - { - $labelWidth = $boundings->width; - $labelHeight = min( - ( $boundings->height ) / count( $labels ) - $legend->spacing, - $legend->symbolSize + 2 * $legend->padding - ); - } - else - { - $labelWidth = ( $boundings->width ) / count( $labels ) - $legend->spacing; - $labelHeight = min( - $boundings->height, - $legend->symbolSize + 2 * $legend->padding - ); - } - - $symbolSize = $labelHeight - 2 * $legend->padding; - - // Draw all labels - $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ); - foreach ( $labels as $label ) - { - $this->elements['legend_url'][$label['label']] = $label['url']; - - $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol( - new ezcGraphBoundings( - $labelPosition->x + $legend->padding, - $labelPosition->y + $legend->padding, - $labelPosition->x + $legend->padding + $symbolSize, - $labelPosition->y + $legend->padding + $symbolSize - ), - $label['color'], - $label['symbol'] - ); - - $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox( - $label['label'], - new ezcGraphCoordinate( - $labelPosition->x + 2 * $legend->padding + $symbolSize, - $labelPosition->y + $legend->padding - ), - $labelWidth - $symbolSize - 3 * $legend->padding, - $labelHeight - 2 * $legend->padding, - ezcGraph::LEFT | ezcGraph::MIDDLE - ); - - $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing ); - $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 ); - } - } - - /** - * Draw box - * - * Box are wrapping each major chart element and draw border, background - * and title to each chart element. - * - * Optionally a padding and margin for each box can be defined. - * - * @param ezcGraphBoundings $boundings Boundings of the box - * @param ezcGraphColor $background Background color - * @param ezcGraphColor $borderColor Border color - * @param int $borderWidth Border width - * @param int $margin Margin - * @param int $padding Padding - * @param mixed $title Title of the box - * @param int $titleSize Size of title in the box - * @return ezcGraphBoundings Remaining inner boundings - */ - public function drawBox( - ezcGraphBoundings $boundings, - ezcGraphColor $background = null, - ezcGraphColor $borderColor = null, - $borderWidth = 0, - $margin = 0, - $padding = 0, - $title = false, - $titleSize = 16 ) - { - // Apply margin - $boundings->x0 += $margin; - $boundings->y0 += $margin; - $boundings->x1 -= $margin; - $boundings->y1 -= $margin; - - if ( $background instanceof ezcGraphColor ) - { - // Draw box background - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $background, - true - ); - } - - if ( ( $borderColor instanceof ezcGraphColor ) && - ( $borderWidth > 0 ) ) - { - // Draw border - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $borderColor, - false, - $borderWidth - ); - - // Reduce local boundings by borderWidth - $boundings->x0 += $borderWidth; - $boundings->y0 += $borderWidth; - $boundings->x1 -= $borderWidth; - $boundings->y1 -= $borderWidth; - } - - // Apply padding - $boundings->x0 += $padding; - $boundings->y0 += $padding; - $boundings->x1 -= $padding; - $boundings->y1 -= $padding; - - // Add box title - if ( $title !== false ) - { - switch ( $this->options->titlePosition ) - { - case ezcGraph::TOP: - $this->driver->drawTextBox( - $title, - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - $boundings->width, - $titleSize, - $this->options->titleAlignement - ); - - $boundings->y0 += $titleSize + $padding; - $boundings->y1 -= $titleSize + $padding; - break; - case ezcGraph::BOTTOM: - $this->driver->drawTextBox( - $title, - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ), - $boundings->width, - $titleSize, - $this->options->titleAlignement - ); - - $boundings->y1 -= $titleSize + $padding; - break; - } - } - - return $boundings; - } - - /** - * Draw text - * - * Draws the provided text in the boundings - * - * @param ezcGraphBoundings $boundings Boundings of text - * @param string $text Text - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawText( - ezcGraphBoundings $boundings, - $text, - $align = ezcGraph::LEFT, - ezcGraphRotation $rotation = null ) - { - $this->driver->drawTextBox( - $text, - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - $boundings->width, - $boundings->height, - $align, - $rotation - ); - } - - /** - * Draw grid line - * - * Draw line for the grid in the chart background - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Color of the grid line - * @return void - */ - public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) - { - $this->driver->drawLine( - $start, - $end, - $color, - 1 - ); - } - - /** - * Draw step line - * - * Draw a step (marker for label position) on a axis. - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Color of the grid line - * @return void - */ - public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) - { - $this->driver->drawLine( - $start, - $end, - $color, - 1 - ); - } - - /** - * Draw axis - * - * Draws an axis form the provided start point to the end point. A specific - * angle of the axis is not required. - * - * For the labeleing of the axis a sorted array with major steps and an - * array with minor steps is expected, which are build like this: - * array( - * array( - * 'position' => (float), - * 'label' => (string), - * ) - * ) - * where the label is optional. - * - * The label renderer class defines how the labels are rendered. For more - * documentation on this topic have a look at the basic label renderer - * class. - * - * Additionally it can be specified if a major and minor grid are rendered - * by defining a color for them. The axis label is used to add a caption - * for the axis. - * - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $start Start point of axis - * @param ezcGraphCoordinate $end Endpoint of axis - * @param ezcGraphChartElementAxis $axis Axis to render - * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer - * @return void - */ - public function drawAxis( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphAxisLabelRenderer $labelClass = null, - ezcGraphBoundings $innerBoundings = null ) - { - // Legacy axis drawing for BC reasons - if ( $innerBoundings === null ) - { - return $this->legacyDrawAxis( $boundings, $start, $end, $axis, $labelClass ); - } - - // Calculate axis start and end points depending on inner boundings - if ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) ) - { - $innerStart = new ezcGraphCoordinate( - $start->x + $boundings->x0, - ( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 : $innerBoundings->y1 ) - ); - $innerEnd = new ezcGraphCoordinate( - $end->x + $boundings->x0, - ( $axis->position === ezcGraph::TOP ? $innerBoundings->y1 : $innerBoundings->y0 ) - ); - } - else - { - $innerStart = new ezcGraphCoordinate( - ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 : $innerBoundings->x1 ), - $start->y + $boundings->y0 - ); - $innerEnd = new ezcGraphCoordinate( - ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x1 : $innerBoundings->x0 ), - $end->y + $boundings->y0 - ); - } - - // Shorten axis, if requested - if ( $this->options->shortAxis ) - { - $start = clone $innerStart; - $end = clone $innerEnd; - } - else - { - $start->x += $boundings->x0; - $start->y += $boundings->y0; - $end->x += $boundings->x0; - $end->y += $boundings->y0; - } - - // Determine normalized direction - $direction = new ezcGraphVector( - $start->x - $end->x, - $start->y - $end->y - ); - $direction->unify(); - - // Draw axis - $this->driver->drawLine( - $start, - $end, - $axis->border, - 1 - ); - - // Draw small arrowhead - $this->drawAxisArrowHead( - $end, - $direction, - max( - $axis->minArrowHeadSize, - min( - $axis->maxArrowHeadSize, - abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) - ) - ), - $axis->border - ); - - // Draw axis label, if set - $this->drawAxisLabel( $end, $innerBoundings, $axis ); - - // If font should not be synchronized, use font configuration from - // each axis - if ( $this->options->syncAxisFonts === false ) - { - $this->driver->options->font = $axis->font; - } - - $labelClass->renderLabels( - $this, - $boundings, - $innerStart, - $innerEnd, - $axis, - $innerBoundings - ); - } - - /** - * Draw axis label - * - * Draw labels at the end of an axis. - * - * @param ezcGraphCoordinate $position - * @param ezcGraphBoundings $boundings - * @param ezcGraphChartElementAxis $axis - * @return void - */ - protected function drawAxisLabel( ezcGraphCoordinate $position, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis ) - { - if ( $axis->label === false ) - { - return; - } - - $width = $boundings->width; - switch ( $axis->position ) - { - case ezcGraph::TOP: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $position->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ), - $position->y - $axis->labelMargin - $axis->labelSize - ), - $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, - $axis->labelSize, - ezcGraph::TOP | ezcGraph::RIGHT - ); - break; - case ezcGraph::BOTTOM: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $position->x + $axis->labelMargin, - $position->y + $axis->labelMargin - ), - $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, - $axis->labelSize, - ezcGraph::TOP | ezcGraph::LEFT - ); - break; - case ezcGraph::LEFT: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $position->x - $width, - $position->y - $axis->labelSize - $axis->labelMargin - ), - $width - $axis->labelMargin, - $axis->labelSize, - ezcGraph::BOTTOM | ezcGraph::RIGHT - ); - break; - case ezcGraph::RIGHT: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $position->x, - $position->y - $axis->labelSize - $axis->labelMargin - ), - $width - $axis->labelMargin, - $axis->labelSize, - ezcGraph::BOTTOM | ezcGraph::LEFT - ); - break; - } - } - - /** - * Draw axis - * - * Draws an axis form the provided start point to the end point. A specific - * angle of the axis is not required. - * - * For the labeleing of the axis a sorted array with major steps and an - * array with minor steps is expected, which are build like this: - * array( - * array( - * 'position' => (float), - * 'label' => (string), - * ) - * ) - * where the label is optional. - * - * The label renderer class defines how the labels are rendered. For more - * documentation on this topic have a look at the basic label renderer - * class. - * - * Additionally it can be specified if a major and minor grid are rendered - * by defining a color for them. The axis label is used to add a caption - * for the axis. - * - * This function is deprecated and will be removed in favor of its - * reimplementation using the innerBoundings parameter. - * - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $start Start point of axis - * @param ezcGraphCoordinate $end Endpoint of axis - * @param ezcGraphChartElementAxis $axis Axis to render - * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer - * @apichange - * @return void - */ - protected function legacyDrawAxis( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphAxisLabelRenderer $labelClass = null ) - { - // Store axis space for use by label renderer - switch ( $axis->position ) - { - case ezcGraph::TOP: - case ezcGraph::BOTTOM: - $this->xAxisSpace = ( $boundings->width ) * $axis->axisSpace; - break; - case ezcGraph::LEFT: - case ezcGraph::RIGHT: - $this->yAxisSpace = ( $boundings->height ) * $axis->axisSpace; - break; - } - - // Clone boundings because of internal modifications - $boundings = clone $boundings; - - $start->x += $boundings->x0; - $start->y += $boundings->y0; - $end->x += $boundings->x0; - $end->y += $boundings->y0; - - // Shorten drawn axis, if requested. - if ( ( $this->options->shortAxis === true ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) ) ) - { - $axisStart = clone $start; - $axisEnd = clone $end; - - $axisStart->y += $boundings->height * $axis->axisSpace * - ( $axis->position === ezcGraph::TOP ? 1 : -1 ); - $axisEnd->y -= $boundings->height * $axis->axisSpace * - ( $axis->position === ezcGraph::TOP ? 1 : -1 ); - } - elseif ( ( $this->options->shortAxis === true ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) ) ) - { - $axisStart = clone $start; - $axisEnd = clone $end; - - $axisStart->x += $boundings->width * $axis->axisSpace * - ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); - $axisEnd->x -= $boundings->width * $axis->axisSpace * - ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); - } - else - { - $axisStart = $start; - $axisEnd = $end; - } - - // Determine normalized direction - $direction = new ezcGraphVector( - $start->x - $end->x, - $start->y - $end->y - ); - $direction->unify(); - - // Draw axis - $this->driver->drawLine( - $axisStart, - $axisEnd, - $axis->border, - 1 - ); - - // Draw small arrowhead - $this->drawAxisArrowHead( - $axisEnd, - $direction, - max( - $axis->minArrowHeadSize, - min( - $axis->maxArrowHeadSize, - abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) - ) - ), - $axis->border - ); - - // Draw axis label, if set - $this->drawAxisLabel( $end, $boundings, $axis ); - - // Collect axis labels and draw, when all axisSpaces are collected - $this->axisLabels[] = array( - 'object' => $labelClass, - 'boundings' => $boundings, - 'start' => clone $start, - 'end' => clone $end, - 'axis' => $axis, - ); - - if ( $this->xAxisSpace && $this->yAxisSpace ) - { - $this->drawAxisLabels(); - } - } - - /** - * Draw all left axis labels - * - * @return void - */ - protected function drawAxisLabels() - { - foreach ( $this->axisLabels as $nr => $axisLabel ) - { - // If font should not be synchronized, use font configuration from - // each axis - if ( $this->options->syncAxisFonts === false ) - { - $this->driver->options->font = $axisLabel['axis']->font; - } - - $start = $axisLabel['start']; - $end = $axisLabel['end']; - - $direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $direction->unify(); - - // Convert elipse to circle for correct angle calculation - $direction->y *= ( $this->xAxisSpace / $this->yAxisSpace ); - $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); - - $movement = new ezcGraphVector( - sin( $angle ) * $this->xAxisSpace * ( $direction->x < 0 ? -1 : 1 ), - cos( $angle ) * $this->yAxisSpace - ); - - $start->x += $movement->x; - $start->y += $movement->y; - $end->x -= $movement->x; - $end->y -= $movement->y; - - $axisLabel['object']->renderLabels( - $this, - $axisLabel['boundings'], - $start, - $end, - $axisLabel['axis'] - ); - - // Prevent from redrawing axis on more then 2 axis. - unset( $this->axisLabels[$nr] ); - } - } - - /** - * Draw background image - * - * Draws a background image at the defined position. If repeat is set the - * background image will be repeated like any texture. - * - * @param ezcGraphBoundings $boundings Boundings for the background image - * @param string $file Filename of background image - * @param int $position Position of background image - * @param int $repeat Type of repetition - * @return void - */ - public function drawBackgroundImage( - ezcGraphBoundings $boundings, - $file, - $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE - $repeat = ezcGraph::NO_REPEAT ) - { - $imageData = getimagesize( $file ); - $imageWidth = $imageData[0]; - $imageHeight = $imageData[1]; - - $imageWidth = min( $imageWidth, $boundings->width ); - $imageHeight = min( $imageHeight, $boundings->height ); - - $imagePosition = new ezcGraphCoordinate( - $boundings->x0, - $boundings->y0 - ); - - // Determine x position - switch ( true ) { - case ( $repeat & ezcGraph::HORIZONTAL ): - // If is repeated on this axis fall back to position zero - case ( $position & ezcGraph::LEFT ): - $imagePosition->x = $boundings->x0; - break; - case ( $position & ezcGraph::RIGHT ): - $imagePosition->x = max( - $boundings->x1 - $imageWidth, - $boundings->x0 - ); - break; - default: - $imagePosition->x = max( - $boundings->x0 + ( $boundings->width - $imageWidth ) / 2, - $boundings->x0 - ); - break; - } - - // Determine y position - switch ( true ) { - case ( $repeat & ezcGraph::VERTICAL ): - // If is repeated on this axis fall back to position zero - case ( $position & ezcGraph::TOP ): - $imagePosition->y = $boundings->y0; - break; - case ( $position & ezcGraph::BOTTOM ): - $imagePosition->y = max( - $boundings->y1 - $imageHeight, - $boundings->y0 - ); - break; - default: - $imagePosition->y = max( - $boundings->y0 + ( $boundings->height - $imageHeight ) / 2, - $boundings->y0 - ); - break; - } - - // Texturize backround based on position and repetition - $position = new ezcGraphCoordinate( - $imagePosition->x, - $imagePosition->y - ); - - do - { - $position->y = $imagePosition->y; - - do - { - $this->driver->drawImage( - $file, - $position, - $imageWidth, - $imageHeight - ); - - $position->y += $imageHeight; - } - while ( ( $position->y < $boundings->y1 ) && - ( $repeat & ezcGraph::VERTICAL ) ); - - $position->x += $imageWidth; - } - while ( ( $position->x < $boundings->x1 ) && - ( $repeat & ezcGraph::HORIZONTAL ) ); - } - - /** - * Call all postprocessing functions - * - * @return void - */ - protected function finish() - { - $this->finishCircleSectors(); - $this->finishPieSegmentLabels(); - $this->finishLineSymbols(); - - return true; - } - - /** - * Reset renderer properties - * - * Reset all renderer properties, which were calculated during the - * rendering process, to offer a clean environment for rerendering. - * - * @return void - */ - protected function resetRenderer() - { - parent::resetRenderer(); - - // Also reset special 2D renderer options - $this->pieSegmentLabels = array( - 0 => array(), - 1 => array(), - ); - $this->pieSegmentBoundings = false; - $this->linePostSymbols = array(); - $this->axisLabels = array(); - $this->circleSectors = array(); - } - - /** - * Render odometer chart - * - * @param ezcGraphBoundings $boundings - * @param ezcGraphChartElementAxis $axis - * @param ezcGraphOdometerChartOptions $options - * @return ezcGraphBoundings - */ - public function drawOdometer( - ezcGraphBoundings $boundings, - ezcGraphChartElementAxis $axis, - ezcGraphOdometerChartOptions $options ) - { - $height = $boundings->height * $options->odometerHeight; - - // Draw axis - $oldAxisSpace = $axis->axisSpace; - $axis->axisSpace = 0; - - $axis->render( $this, $boundings ); - - // Reset axisspaces to correct values - $this->xAxisSpace = $boundings->width * $oldAxisSpace; - $this->yAxisSpace = ( $boundings->height - $height ) / 2; - - $this->drawAxisLabels(); - - // Reduce size of chart boundings respecting requested odometer height - $boundings->x0 += $this->xAxisSpace; - $boundings->x1 -= $this->xAxisSpace; - $boundings->y0 += $this->yAxisSpace; - $boundings->y1 -= $this->yAxisSpace; - - $gradient = new ezcGraphLinearGradient( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - $options->startColor, - $options->endColor - ); - - // Simply draw box with gradient and optional border - $this->drawBox( - $boundings, - $gradient, - $options->borderColor, - $options->borderWidth - ); - - // Return modified chart boundings - return $boundings; - } - - /** - * Draw a single odometer marker. - * - * @param ezcGraphBoundings $boundings - * @param ezcGraphCoordinate $position - * @param int $symbol - * @param ezcGraphColor $color - * @param int $width - */ - public function drawOdometerMarker( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $position, - $symbol, - ezcGraphColor $color, - $width ) - { - $this->driver->drawLine( - new ezcGraphCoordinate( - $xPos = $boundings->x0 + ( $position->x * $boundings->width ), - $boundings->y0 - ), - new ezcGraphCoordinate( - $xPos, - $boundings->y1 - ), - $color, - $width - ); - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteBlack(); + * $graph->title = 'Access statistics'; + * $graph->options->label = '%2$d (%3$.1f%%)'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * $graph->data['Access statistics']->highlight['Explorer'] = true; + * + * // $graph->renderer = new ezcGraphRenderer2d(); + * + * $graph->renderer->options->moveOut = .2; + * + * $graph->renderer->options->pieChartOffset = 63; + * + * $graph->renderer->options->pieChartGleam = .3; + * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; + * $graph->renderer->options->pieChartGleamBorder = 2; + * + * $graph->renderer->options->pieChartShadowSize = 3; + * $graph->renderer->options->pieChartShadowColor = '#000000'; + * + * $graph->renderer->options->legendSymbolGleam = .5; + * $graph->renderer->options->legendSymbolGleamSize = .9; + * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartSymbolColor = '#BABDB688'; + * + * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' ); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphRenderer2d + extends + ezcGraphRenderer + implements + ezcGraphRadarRenderer, ezcGraphStackedBarsRenderer, ezcGraphOdometerRenderer +{ + + /** + * Pie segment labels divided into two array, containing the labels on the + * left and right side of the pie chart center. + * + * @var array + */ + protected $pieSegmentLabels = array( + 0 => array(), + 1 => array(), + ); + + /** + * Contains the boundings used for pie segments + * + * @var ezcGraphBoundings + */ + protected $pieSegmentBoundings = false; + + /** + * Array with symbols for post processing, which ensures, that the symbols + * are rendered topmost. + * + * @var array + */ + protected $linePostSymbols = array(); + + /** + * Options + * + * @var ezcGraphRenderer2dOptions + */ + protected $options; + + /** + * Collect axis labels, so that the axis are drawn, when all axis spaces + * are known. + * + * @var array + */ + protected $axisLabels = array(); + + /** + * Collects circle sectors to draw shadow in background of all circle + * sectors. + * + * @var array + */ + protected $circleSectors = array(); + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->options = new ezcGraphRenderer2dOptions( $options ); + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'options': + return $this->options; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Draw pie segment + * + * Draws a single pie segment + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of pie segment + * @param float $startAngle Start angle + * @param float $endAngle End angle + * @param mixed $label Label of pie segment + * @param bool $moveOut Move out from middle for hilighting + * @return void + */ + public function drawPieSegment( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + $startAngle = .0, + $endAngle = 360., + $label = false, + $moveOut = false ) + { + // Apply offset + $startAngle += $this->options->pieChartOffset; + $endAngle += $this->options->pieChartOffset; + + // Calculate position and size of pie + $center = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) / 2, + $boundings->y0 + ( $boundings->height ) / 2 + ); + + // Limit radius to fourth of width and half of height at maximum + $radius = min( + ( $boundings->width ) * $this->options->pieHorizontalSize, + ( $boundings->height ) * $this->options->pieVerticalSize + ); + + // Move pie segment out of the center + if ( $moveOut ) + { + $direction = ( $endAngle + $startAngle ) / 2; + + $center = new ezcGraphCoordinate( + $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ), + $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) + ); + } + + // Add circle sector to queue + $this->circleSectors[] = array( + 'center' => $center, + 'context' => $context, + 'width' => $radius * 2 * ( 1 - $this->options->moveOut ), + 'height' => $radius * 2 * ( 1 - $this->options->moveOut ), + 'start' => $startAngle, + 'end' => $endAngle, + 'color' => $color, + ); + + if ( $label ) + { + // Determine position of label + $direction = ( $endAngle + $startAngle ) / 2; + $pieSegmentCenter = new ezcGraphCoordinate( + $center->x + cos( deg2rad( $direction ) ) * $radius, + $center->y + sin( deg2rad( $direction ) ) * $radius + ); + + // Split labels up into left an right size and index them on their + // y position + $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array( + new ezcGraphCoordinate( + $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3, + $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3 + ), + $label, + $context + ); + } + + if ( !$this->pieSegmentBoundings ) + { + $this->pieSegmentBoundings = $boundings; + } + } + + /** + * Draws the collected circle sectors + * + * All circle sectors are collected and drawn later to be able to render + * the shadows of the pie segments in the back of all pie segments. + * + * @return void + */ + protected function finishCircleSectors() + { + // Add circle sector sides to simple z buffer prioriry list + if ( $this->options->pieChartShadowSize > 0 ) + { + foreach ( $this->circleSectors as $circleSector ) + { + $this->driver->drawCircleSector( + new ezcGraphCoordinate( + $circleSector['center']->x + $this->options->pieChartShadowSize, + $circleSector['center']->y + $this->options->pieChartShadowSize + ), + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $this->options->pieChartShadowColor->transparent( $this->options->pieChartShadowTransparency ), + true + ); + } + } + + foreach ( $this->circleSectors as $circleSector ) + { + // Draw circle sector + $this->addElementReference( + $circleSector['context'], + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $circleSector['color'], + true + ) + ); + + $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $darkenedColor, + false + ); + + if ( $this->options->pieChartGleam !== false ) + { + $gradient = new ezcGraphLinearGradient( + $circleSector['center'], + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y - $circleSector['height'] / 2 + ), + $this->options->pieChartGleamColor->transparent( 1 ), + $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) + ); + + $this->addElementReference( $circleSector['context'], + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'] - $this->options->pieChartGleamBorder * 2, + $circleSector['height'] - $this->options->pieChartGleamBorder * 2, + $circleSector['start'], + $circleSector['end'], + $gradient, + true + ) + ); + + $gradient = new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $circleSector['height'] / 4 + ), + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $circleSector['height'] / 2 + ), + $this->options->pieChartGleamColor->transparent( 1 ), + $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) + ); + + $this->addElementReference( $circleSector['context'], + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'] - $this->options->pieChartGleamBorder * 2, + $circleSector['height'] - $this->options->pieChartGleamBorder * 2, + $circleSector['start'], + $circleSector['end'], + $gradient, + true + ) + ); + } + } + } + + /** + * Draws the collected pie segment labels + * + * All labels are collected and drawn later to be able to partition the + * available space for the labels woth knowledge of the overall label + * count and their required size and optimal position. + * + * @return void + */ + protected function finishPieSegmentLabels() + { + if ( $this->pieSegmentBoundings === false ) + { + return true; + } + + $boundings = $this->pieSegmentBoundings; + + // Calculate position and size of pie + $center = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) / 2, + $boundings->y0 + ( $boundings->height ) / 2 + ); + + // Limit radius to fourth of width and half of height at maximum + $radius = min( + ( $boundings->width ) * $this->options->pieHorizontalSize, + ( $boundings->height ) * $this->options->pieVerticalSize + ); + + $pieChartHeight = min( + $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4, + $boundings->height + ); + $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2; + + // Calculate maximum height of labels + $labelHeight = min( + ( count( $this->pieSegmentLabels[0] ) + ? $pieChartHeight / count( $this->pieSegmentLabels[0] ) + : $pieChartHeight + ), + ( count( $this->pieSegmentLabels[1] ) + ? $pieChartHeight / count( $this->pieSegmentLabels[1] ) + : $pieChartHeight + ), + ( $pieChartHeight ) * $this->options->maxLabelHeight + ); + + $symbolSize = $this->options->symbolSize; + + foreach ( $this->pieSegmentLabels as $side => $labelPart ) + { + $minHeight = $pieChartYPosition; + $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight; + + // Sort to draw topmost label first + ksort( $labelPart ); + $sign = ( $side ? -1 : 1 ); + + foreach ( $labelPart as $height => $label ) + { + if ( ( $height - $labelHeight / 2 ) > $minHeight ) + { + $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight ); + $minHeight += $share; + $toShare -= $share; + } + + // Determine position of label + $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare; + $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius; + + $labelPosition = new ezcGraphCoordinate( + $center->x - + $sign * ( + abs( $verticalDistance ) > 1 + // If vertical distance to center is greater then the + // radius, use the centerline for the horizontal + // position + ? max ( + 5, + abs( $label[0]->x - $center->x ) + ) + // Else place the label outside of the pie chart + : ( cos ( asin ( $verticalDistance ) ) * $radius + + $symbolSize * (int) $this->options->showSymbol + ) + ), + $minHeight + $labelHeight / 2 + ); + + if ( $this->options->showSymbol ) + { + // Draw label + $this->driver->drawLine( + $label[0], + $labelPosition, + $this->options->pieChartSymbolColor, + 1 + ); + + $this->driver->drawCircle( + $label[0], + $symbolSize, + $symbolSize, + $this->options->pieChartSymbolColor, + true + ); + $this->driver->drawCircle( + $labelPosition, + $symbolSize, + $symbolSize, + $this->options->pieChartSymbolColor, + true + ); + } + + $this->addElementReference( + $label[2], + $this->driver->drawTextBox( + $label[1], + new ezcGraphCoordinate( + ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ), + $minHeight + ), + ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ), + $labelHeight, + ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE + ) + ); + + // Add used space to minHeight + $minHeight += $labelHeight; + } + } + } + + /** + * Draw the collected line symbols + * + * Symbols for the data lines are collected and delayed to ensure that + * they are not covered and hidden by other data lines. + * + * @return void + */ + protected function finishLineSymbols() + { + foreach ( $this->linePostSymbols as $symbol ) + { + $this->addElementReference( + $symbol['context'], + $this->drawSymbol( + $symbol['boundings'], + $symbol['color'], + $symbol['symbol'] + ) + ); + } + } + + /** + * Draw bar + * + * Draws a bar as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $position Position of data point + * @param float $stepSize Space which can be used for bars + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param float $axisPosition Position of axis for drawing filled lines + * @return void + */ + public function drawBar( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $stepSize, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + $axisPosition = 0. ) + { + // Apply margin + $margin = $stepSize * $this->options->barMargin; + $padding = $stepSize * $this->options->barPadding; + $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; + $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2; + + $barPointArray = array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset, + $boundings->y0 + ( $boundings->height ) * $position->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, + $boundings->y0 + ( $boundings->height ) * $position->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + ); + + $this->addElementReference( + $context, + $this->driver->drawPolygon( + $barPointArray, + $color, + true + ) + ); + + if ( $this->options->dataBorder > 0 ) + { + $darkened = $color->darken( $this->options->dataBorder ); + $this->driver->drawPolygon( + $barPointArray, + $darkened, + false, + 1 + ); + } + } + + /** + * Draw stacked bar + * + * Draws a stacked bar part as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $start + * @param ezcGraphCoordinate $position + * @param float $stepSize Space which can be used for bars + * @param int $symbol Symbol to draw for line + * @param float $axisPosition Position of axis for drawing filled lines + * @return void + */ + public function drawStackedBar( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $start, + ezcGraphCoordinate $position, + $stepSize, + $symbol = ezcGraph::NO_SYMBOL, + $axisPosition = 0. ) + { + // Apply margin + $margin = $stepSize * $this->options->barMargin; + $barWidth = $stepSize - $margin; + $offset = - $stepSize / 2 + $margin / 2; + + $barPointArray = array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset, + $boundings->y0 + ( $boundings->height ) * $start->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset, + $boundings->y0 + ( $boundings->height ) * $position->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, + $boundings->y0 + ( $boundings->height ) * $position->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth, + $boundings->y0 + ( $boundings->height ) * $start->y + ), + ); + + $this->addElementReference( + $context, + $this->driver->drawPolygon( + $barPointArray, + $color, + true + ) + ); + + if ( $this->options->dataBorder > 0 ) + { + $darkened = $color->darken( $this->options->dataBorder ); + $this->driver->drawPolygon( + $barPointArray, + $darkened, + false, + 1 + ); + } + } + + /** + * Draw data line + * + * Draws a line as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $start Starting point + * @param ezcGraphCoordinate $end Ending point + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor + * @param ezcGraphColor $fillColor Color to fill line with + * @param float $axisPosition Position of axis for drawing filled lines + * @param float $thickness Line thickness + * @return void + */ + public function drawDataLine( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + ezcGraphColor $symbolColor = null, + ezcGraphColor $fillColor = null, + $axisPosition = 0., + $thickness = 1. ) + { + // Perhaps fill up line + if ( $fillColor !== null && + $start->x != $end->x ) + { + $startValue = $axisPosition - $start->y; + $endValue = $axisPosition - $end->y; + + if ( ( $startValue == 0 ) || + ( $endValue == 0 ) || + ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) ) + { + // Values have the same sign or are on the axis + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $start->x, + $boundings->y0 + ( $boundings->height ) * $start->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x, + $boundings->y0 + ( $boundings->height ) * $end->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $start->x, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + ), + $fillColor, + true + ); + } + else + { + // values are on differente sides of the axis - split the filled polygon + $startDiff = abs( $axisPosition - $start->y ); + $endDiff = abs( $axisPosition - $end->y ); + + $cuttingPosition = $startDiff / ( $endDiff + $startDiff ); + $cuttingPoint = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $cuttingPosition, + $axisPosition + ); + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $start->x, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $start->x, + $boundings->y0 + ( $boundings->height ) * $start->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x, + $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y + ), + ), + $fillColor, + true + ); + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x, + $boundings->y0 + ( $boundings->height ) * $axisPosition + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x, + $boundings->y0 + ( $boundings->height ) * $end->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x, + $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y + ), + ), + $fillColor, + true + ); + } + } + + // Draw line + $this->driver->drawLine( + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $start->x, + $boundings->y0 + ( $boundings->height ) * $start->y + ), + new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x, + $boundings->y0 + ( $boundings->height ) * $end->y + ), + $color, + $thickness + ); + + // Draw line symbol + if ( $symbol !== ezcGraph::NO_SYMBOL ) + { + if ( $symbolColor === null ) + { + $symbolColor = $color; + } + + $this->linePostSymbols[] = array( + 'boundings' => new ezcGraphBoundings( + $boundings->x0 + ( $boundings->width ) * $end->x - $this->options->symbolSize / 2, + $boundings->y0 + ( $boundings->height ) * $end->y - $this->options->symbolSize / 2, + $boundings->x0 + ( $boundings->width ) * $end->x + $this->options->symbolSize / 2, + $boundings->y0 + ( $boundings->height ) * $end->y + $this->options->symbolSize / 2 + ), + 'color' => $symbolColor, + 'context' => $context, + 'symbol' => $symbol, + ); + } + } + + /** + * Returns a coordinate in the given bounding box for the given angle + * radius with the center as base point. + * + * @param ezcGraphBoundings $boundings + * @param ezcGraphCoordinate $center + * @param float $angle + * @param float $radius + * @return float + */ + protected function getCoordinateFromAngleAndRadius( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $center, + $angle, + $radius + ) + { + $direction = new ezcGraphCoordinate( + sin( $angle ) * $boundings->width / 2, + -cos( $angle ) * $boundings->height / 2 + ); + + $offset = new ezcGraphCoordinate( + sin( $angle ) * $this->xAxisSpace, + -cos( $angle ) * $this->yAxisSpace + ); + + $direction->x -= 2 * $offset->x; + $direction->y -= 2 * $offset->y; + + return new ezcGraphCoordinate( + $boundings->x0 + + $center->x + + $offset->x + + $direction->x * $radius, + $boundings->y0 + + $center->y + + $offset->y + + $direction->y * $radius + ); + } + + /** + * Draw radar chart data line + * + * Draws a line as a data element in a radar chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $center Center of radar chart + * @param ezcGraphCoordinate $start Starting point + * @param ezcGraphCoordinate $end Ending point + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor + * @param ezcGraphColor $fillColor Color to fill line with + * @param float $thickness Line thickness + * @return void + */ + public function drawRadarDataLine( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $center, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + ezcGraphColor $symbolColor = null, + ezcGraphColor $fillColor = null, + $thickness = 1. + ) + { + // Calculate line points from chart coordinates + $start = $this->getCoordinateFromAngleAndRadius( + $boundings, + $center, + $start->x * 2 * M_PI, + $start->y + ); + $end = $this->getCoordinateFromAngleAndRadius( + $boundings, + $center, + $end->x * 2 * M_PI, + $end->y + ); + + // Fill line + if ( $fillColor !== null ) + { + $this->driver->drawPolygon( + array( + $start, + $end, + new ezcGraphCoordinate( + $boundings->x0 + $center->x, + $boundings->y0 + $center->y + ), + ), + $fillColor, + true + ); + } + + // Draw line + $this->driver->drawLine( + $start, + $end, + $color, + $thickness + ); + + if ( $symbol !== ezcGraph::NO_SYMBOL ) + { + $this->drawSymbol( + new ezcGraphBoundings( + $end->x - $this->options->symbolSize / 2, + $end->y - $this->options->symbolSize / 2, + $end->x + $this->options->symbolSize / 2, + $end->y + $this->options->symbolSize / 2 + ), + $symbolColor, + $symbol + ); + } + } + + /** + * Draws a highlight textbox for a datapoint. + * + * A highlight textbox for line and bar charts means a box with the current + * value in the graph. + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphCoordinate $end Ending point + * @param float $axisPosition Position of axis for drawing filled lines + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param ezcGraphFontOptions $font Font used for highlight string + * @param string $text Acutual value + * @param int $size Size of highlight text + * @param ezcGraphColor $markLines + * @param int $xOffset + * @param int $yOffset + * @param float $stepSize + * @param int $type + * @return void + */ + public function drawDataHighlightText( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphCoordinate $end, + $axisPosition = 0., + $dataNumber = 1, + $dataCount = 1, + ezcGraphFontOptions $font, + $text, + $size, + ezcGraphColor $markLines = null, + $xOffset = 0, + $yOffset = 0, + $stepSize = 0., + $type = ezcGraph::LINE ) + { + // Bar specific calculations + if ( $type !== ezcGraph::LINE ) + { + $margin = $stepSize * $this->options->barMargin; + $padding = $stepSize * $this->options->barPadding; + $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; + $offset = -( $dataNumber + ( $dataCount - 1 ) / -2 ) * ( $barWidth + $padding ); + } + + $this->driver->options->font = $font; + $width = $boundings->width / $dataCount; + + $dataPoint = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->width ) * $end->x + $xOffset + + ( $type === ezcGraph::LINE ? 0 : $offset ), + $boundings->y0 + ( $boundings->height ) * $end->y + $yOffset + ); + + if ( $end->y < $axisPosition ) + { + $this->driver->drawTextBox( + $text, + new ezcGraphCoordinate( + $dataPoint->x - $width / 2, + $dataPoint->y - $size - $font->padding - $this->options->symbolSize + ), + $width, + $size, + ezcGraph::CENTER | ezcGraph::BOTTOM + ); + } + else + { + $this->driver->drawTextBox( + $text, + new ezcGraphCoordinate( + $dataPoint->x - $width / 2, + $dataPoint->y + $font->padding + $this->options->symbolSize + ), + $width, + $size, + ezcGraph::CENTER | ezcGraph::TOP + ); + } + } + + /** + * Draw legend + * + * Will draw a legend in the bounding box + * + * @param ezcGraphBoundings $boundings Bounding of legend + * @param ezcGraphChartElementLegend $legend Legend to draw; + * @param int $type Type of legend: Protrait or landscape + * @return void + */ + public function drawLegend( + ezcGraphBoundings $boundings, + ezcGraphChartElementLegend $legend, + $type = ezcGraph::VERTICAL ) + { + $labels = $legend->labels; + + // Calculate boundings of each label + if ( $type & ezcGraph::VERTICAL ) + { + $labelWidth = $boundings->width; + $labelHeight = min( + ( $boundings->height ) / count( $labels ) - $legend->spacing, + $legend->symbolSize + 2 * $legend->padding + ); + } + else + { + $labelWidth = ( $boundings->width ) / count( $labels ) - $legend->spacing; + $labelHeight = min( + $boundings->height, + $legend->symbolSize + 2 * $legend->padding + ); + } + + $symbolSize = $labelHeight - 2 * $legend->padding; + + // Draw all labels + $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ); + foreach ( $labels as $label ) + { + $this->elements['legend_url'][$label['label']] = $label['url']; + + $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol( + new ezcGraphBoundings( + $labelPosition->x + $legend->padding, + $labelPosition->y + $legend->padding, + $labelPosition->x + $legend->padding + $symbolSize, + $labelPosition->y + $legend->padding + $symbolSize + ), + $label['color'], + $label['symbol'] + ); + + $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox( + $label['label'], + new ezcGraphCoordinate( + $labelPosition->x + 2 * $legend->padding + $symbolSize, + $labelPosition->y + $legend->padding + ), + $labelWidth - $symbolSize - 3 * $legend->padding, + $labelHeight - 2 * $legend->padding, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing ); + $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 ); + } + } + + /** + * Draw box + * + * Box are wrapping each major chart element and draw border, background + * and title to each chart element. + * + * Optionally a padding and margin for each box can be defined. + * + * @param ezcGraphBoundings $boundings Boundings of the box + * @param ezcGraphColor $background Background color + * @param ezcGraphColor $borderColor Border color + * @param int $borderWidth Border width + * @param int $margin Margin + * @param int $padding Padding + * @param mixed $title Title of the box + * @param int $titleSize Size of title in the box + * @return ezcGraphBoundings Remaining inner boundings + */ + public function drawBox( + ezcGraphBoundings $boundings, + ezcGraphColor $background = null, + ezcGraphColor $borderColor = null, + $borderWidth = 0, + $margin = 0, + $padding = 0, + $title = false, + $titleSize = 16 ) + { + // Apply margin + $boundings->x0 += $margin; + $boundings->y0 += $margin; + $boundings->x1 -= $margin; + $boundings->y1 -= $margin; + + if ( $background instanceof ezcGraphColor ) + { + // Draw box background + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $background, + true + ); + } + + if ( ( $borderColor instanceof ezcGraphColor ) && + ( $borderWidth > 0 ) ) + { + // Draw border + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $borderColor, + false, + $borderWidth + ); + + // Reduce local boundings by borderWidth + $boundings->x0 += $borderWidth; + $boundings->y0 += $borderWidth; + $boundings->x1 -= $borderWidth; + $boundings->y1 -= $borderWidth; + } + + // Apply padding + $boundings->x0 += $padding; + $boundings->y0 += $padding; + $boundings->x1 -= $padding; + $boundings->y1 -= $padding; + + // Add box title + if ( $title !== false ) + { + switch ( $this->options->titlePosition ) + { + case ezcGraph::TOP: + $this->driver->drawTextBox( + $title, + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + $boundings->width, + $titleSize, + $this->options->titleAlignement + ); + + $boundings->y0 += $titleSize + $padding; + $boundings->y1 -= $titleSize + $padding; + break; + case ezcGraph::BOTTOM: + $this->driver->drawTextBox( + $title, + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ), + $boundings->width, + $titleSize, + $this->options->titleAlignement + ); + + $boundings->y1 -= $titleSize + $padding; + break; + } + } + + return $boundings; + } + + /** + * Draw text + * + * Draws the provided text in the boundings + * + * @param ezcGraphBoundings $boundings Boundings of text + * @param string $text Text + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawText( + ezcGraphBoundings $boundings, + $text, + $align = ezcGraph::LEFT, + ezcGraphRotation $rotation = null ) + { + $this->driver->drawTextBox( + $text, + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + $boundings->width, + $boundings->height, + $align, + $rotation + ); + } + + /** + * Draw grid line + * + * Draw line for the grid in the chart background + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Color of the grid line + * @return void + */ + public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) + { + $this->driver->drawLine( + $start, + $end, + $color, + 1 + ); + } + + /** + * Draw step line + * + * Draw a step (marker for label position) on a axis. + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Color of the grid line + * @return void + */ + public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) + { + $this->driver->drawLine( + $start, + $end, + $color, + 1 + ); + } + + /** + * Draw axis + * + * Draws an axis form the provided start point to the end point. A specific + * angle of the axis is not required. + * + * For the labeleing of the axis a sorted array with major steps and an + * array with minor steps is expected, which are build like this: + * array( + * array( + * 'position' => (float), + * 'label' => (string), + * ) + * ) + * where the label is optional. + * + * The label renderer class defines how the labels are rendered. For more + * documentation on this topic have a look at the basic label renderer + * class. + * + * Additionally it can be specified if a major and minor grid are rendered + * by defining a color for them. The axis label is used to add a caption + * for the axis. + * + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $start Start point of axis + * @param ezcGraphCoordinate $end Endpoint of axis + * @param ezcGraphChartElementAxis $axis Axis to render + * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer + * @return void + */ + public function drawAxis( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphAxisLabelRenderer $labelClass = null, + ezcGraphBoundings $innerBoundings = null ) + { + // Legacy axis drawing for BC reasons + if ( $innerBoundings === null ) + { + return $this->legacyDrawAxis( $boundings, $start, $end, $axis, $labelClass ); + } + + // Calculate axis start and end points depending on inner boundings + if ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) ) + { + $innerStart = new ezcGraphCoordinate( + $start->x + $boundings->x0, + ( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 : $innerBoundings->y1 ) + ); + $innerEnd = new ezcGraphCoordinate( + $end->x + $boundings->x0, + ( $axis->position === ezcGraph::TOP ? $innerBoundings->y1 : $innerBoundings->y0 ) + ); + } + else + { + $innerStart = new ezcGraphCoordinate( + ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 : $innerBoundings->x1 ), + $start->y + $boundings->y0 + ); + $innerEnd = new ezcGraphCoordinate( + ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x1 : $innerBoundings->x0 ), + $end->y + $boundings->y0 + ); + } + + // Shorten axis, if requested + if ( $this->options->shortAxis ) + { + $start = clone $innerStart; + $end = clone $innerEnd; + } + else + { + $start->x += $boundings->x0; + $start->y += $boundings->y0; + $end->x += $boundings->x0; + $end->y += $boundings->y0; + } + + // Determine normalized direction + $direction = new ezcGraphVector( + $start->x - $end->x, + $start->y - $end->y + ); + $direction->unify(); + + // Draw axis + $this->driver->drawLine( + $start, + $end, + $axis->border, + 1 + ); + + // Draw small arrowhead + $this->drawAxisArrowHead( + $end, + $direction, + max( + $axis->minArrowHeadSize, + min( + $axis->maxArrowHeadSize, + abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) + ) + ), + $axis->border + ); + + // Draw axis label, if set + $this->drawAxisLabel( $end, $innerBoundings, $axis ); + + // If font should not be synchronized, use font configuration from + // each axis + if ( $this->options->syncAxisFonts === false ) + { + $this->driver->options->font = $axis->font; + } + + $labelClass->renderLabels( + $this, + $boundings, + $innerStart, + $innerEnd, + $axis, + $innerBoundings + ); + } + + /** + * Draw axis label + * + * Draw labels at the end of an axis. + * + * @param ezcGraphCoordinate $position + * @param ezcGraphBoundings $boundings + * @param ezcGraphChartElementAxis $axis + * @return void + */ + protected function drawAxisLabel( ezcGraphCoordinate $position, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis ) + { + if ( $axis->label === false ) + { + return; + } + + $width = $boundings->width; + switch ( $axis->position ) + { + case ezcGraph::TOP: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $position->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ), + $position->y - $axis->labelMargin - $axis->labelSize + ), + $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, + $axis->labelSize, + ezcGraph::TOP | ezcGraph::RIGHT + ); + break; + case ezcGraph::BOTTOM: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $position->x + $axis->labelMargin, + $position->y + $axis->labelMargin + ), + $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, + $axis->labelSize, + ezcGraph::TOP | ezcGraph::LEFT + ); + break; + case ezcGraph::LEFT: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $position->x - $width, + $position->y - $axis->labelSize - $axis->labelMargin + ), + $width - $axis->labelMargin, + $axis->labelSize, + ezcGraph::BOTTOM | ezcGraph::RIGHT + ); + break; + case ezcGraph::RIGHT: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $position->x, + $position->y - $axis->labelSize - $axis->labelMargin + ), + $width - $axis->labelMargin, + $axis->labelSize, + ezcGraph::BOTTOM | ezcGraph::LEFT + ); + break; + } + } + + /** + * Draw axis + * + * Draws an axis form the provided start point to the end point. A specific + * angle of the axis is not required. + * + * For the labeleing of the axis a sorted array with major steps and an + * array with minor steps is expected, which are build like this: + * array( + * array( + * 'position' => (float), + * 'label' => (string), + * ) + * ) + * where the label is optional. + * + * The label renderer class defines how the labels are rendered. For more + * documentation on this topic have a look at the basic label renderer + * class. + * + * Additionally it can be specified if a major and minor grid are rendered + * by defining a color for them. The axis label is used to add a caption + * for the axis. + * + * This function is deprecated and will be removed in favor of its + * reimplementation using the innerBoundings parameter. + * + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $start Start point of axis + * @param ezcGraphCoordinate $end Endpoint of axis + * @param ezcGraphChartElementAxis $axis Axis to render + * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer + * @apichange + * @return void + */ + protected function legacyDrawAxis( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphAxisLabelRenderer $labelClass = null ) + { + // Store axis space for use by label renderer + switch ( $axis->position ) + { + case ezcGraph::TOP: + case ezcGraph::BOTTOM: + $this->xAxisSpace = ( $boundings->width ) * $axis->axisSpace; + break; + case ezcGraph::LEFT: + case ezcGraph::RIGHT: + $this->yAxisSpace = ( $boundings->height ) * $axis->axisSpace; + break; + } + + // Clone boundings because of internal modifications + $boundings = clone $boundings; + + $start->x += $boundings->x0; + $start->y += $boundings->y0; + $end->x += $boundings->x0; + $end->y += $boundings->y0; + + // Shorten drawn axis, if requested. + if ( ( $this->options->shortAxis === true ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) ) ) + { + $axisStart = clone $start; + $axisEnd = clone $end; + + $axisStart->y += $boundings->height * $axis->axisSpace * + ( $axis->position === ezcGraph::TOP ? 1 : -1 ); + $axisEnd->y -= $boundings->height * $axis->axisSpace * + ( $axis->position === ezcGraph::TOP ? 1 : -1 ); + } + elseif ( ( $this->options->shortAxis === true ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) ) ) + { + $axisStart = clone $start; + $axisEnd = clone $end; + + $axisStart->x += $boundings->width * $axis->axisSpace * + ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); + $axisEnd->x -= $boundings->width * $axis->axisSpace * + ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); + } + else + { + $axisStart = $start; + $axisEnd = $end; + } + + // Determine normalized direction + $direction = new ezcGraphVector( + $start->x - $end->x, + $start->y - $end->y + ); + $direction->unify(); + + // Draw axis + $this->driver->drawLine( + $axisStart, + $axisEnd, + $axis->border, + 1 + ); + + // Draw small arrowhead + $this->drawAxisArrowHead( + $axisEnd, + $direction, + max( + $axis->minArrowHeadSize, + min( + $axis->maxArrowHeadSize, + abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) + ) + ), + $axis->border + ); + + // Draw axis label, if set + $this->drawAxisLabel( $end, $boundings, $axis ); + + // Collect axis labels and draw, when all axisSpaces are collected + $this->axisLabels[] = array( + 'object' => $labelClass, + 'boundings' => $boundings, + 'start' => clone $start, + 'end' => clone $end, + 'axis' => $axis, + ); + + if ( $this->xAxisSpace && $this->yAxisSpace ) + { + $this->drawAxisLabels(); + } + } + + /** + * Draw all left axis labels + * + * @return void + */ + protected function drawAxisLabels() + { + foreach ( $this->axisLabels as $nr => $axisLabel ) + { + // If font should not be synchronized, use font configuration from + // each axis + if ( $this->options->syncAxisFonts === false ) + { + $this->driver->options->font = $axisLabel['axis']->font; + } + + $start = $axisLabel['start']; + $end = $axisLabel['end']; + + $direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $direction->unify(); + + // Convert elipse to circle for correct angle calculation + $direction->y *= ( $this->xAxisSpace / $this->yAxisSpace ); + $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); + + $movement = new ezcGraphVector( + sin( $angle ) * $this->xAxisSpace * ( $direction->x < 0 ? -1 : 1 ), + cos( $angle ) * $this->yAxisSpace + ); + + $start->x += $movement->x; + $start->y += $movement->y; + $end->x -= $movement->x; + $end->y -= $movement->y; + + $axisLabel['object']->renderLabels( + $this, + $axisLabel['boundings'], + $start, + $end, + $axisLabel['axis'] + ); + + // Prevent from redrawing axis on more then 2 axis. + unset( $this->axisLabels[$nr] ); + } + } + + /** + * Draw background image + * + * Draws a background image at the defined position. If repeat is set the + * background image will be repeated like any texture. + * + * @param ezcGraphBoundings $boundings Boundings for the background image + * @param string $file Filename of background image + * @param int $position Position of background image + * @param int $repeat Type of repetition + * @return void + */ + public function drawBackgroundImage( + ezcGraphBoundings $boundings, + $file, + $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE + $repeat = ezcGraph::NO_REPEAT ) + { + $imageData = getimagesize( $file ); + $imageWidth = $imageData[0]; + $imageHeight = $imageData[1]; + + $imageWidth = min( $imageWidth, $boundings->width ); + $imageHeight = min( $imageHeight, $boundings->height ); + + $imagePosition = new ezcGraphCoordinate( + $boundings->x0, + $boundings->y0 + ); + + // Determine x position + switch ( true ) { + case ( $repeat & ezcGraph::HORIZONTAL ): + // If is repeated on this axis fall back to position zero + case ( $position & ezcGraph::LEFT ): + $imagePosition->x = $boundings->x0; + break; + case ( $position & ezcGraph::RIGHT ): + $imagePosition->x = max( + $boundings->x1 - $imageWidth, + $boundings->x0 + ); + break; + default: + $imagePosition->x = max( + $boundings->x0 + ( $boundings->width - $imageWidth ) / 2, + $boundings->x0 + ); + break; + } + + // Determine y position + switch ( true ) { + case ( $repeat & ezcGraph::VERTICAL ): + // If is repeated on this axis fall back to position zero + case ( $position & ezcGraph::TOP ): + $imagePosition->y = $boundings->y0; + break; + case ( $position & ezcGraph::BOTTOM ): + $imagePosition->y = max( + $boundings->y1 - $imageHeight, + $boundings->y0 + ); + break; + default: + $imagePosition->y = max( + $boundings->y0 + ( $boundings->height - $imageHeight ) / 2, + $boundings->y0 + ); + break; + } + + // Texturize backround based on position and repetition + $position = new ezcGraphCoordinate( + $imagePosition->x, + $imagePosition->y + ); + + do + { + $position->y = $imagePosition->y; + + do + { + $this->driver->drawImage( + $file, + $position, + $imageWidth, + $imageHeight + ); + + $position->y += $imageHeight; + } + while ( ( $position->y < $boundings->y1 ) && + ( $repeat & ezcGraph::VERTICAL ) ); + + $position->x += $imageWidth; + } + while ( ( $position->x < $boundings->x1 ) && + ( $repeat & ezcGraph::HORIZONTAL ) ); + } + + /** + * Call all postprocessing functions + * + * @return void + */ + protected function finish() + { + $this->finishCircleSectors(); + $this->finishPieSegmentLabels(); + $this->finishLineSymbols(); + + return true; + } + + /** + * Reset renderer properties + * + * Reset all renderer properties, which were calculated during the + * rendering process, to offer a clean environment for rerendering. + * + * @return void + */ + protected function resetRenderer() + { + parent::resetRenderer(); + + // Also reset special 2D renderer options + $this->pieSegmentLabels = array( + 0 => array(), + 1 => array(), + ); + $this->pieSegmentBoundings = false; + $this->linePostSymbols = array(); + $this->axisLabels = array(); + $this->circleSectors = array(); + } + + /** + * Render odometer chart + * + * @param ezcGraphBoundings $boundings + * @param ezcGraphChartElementAxis $axis + * @param ezcGraphOdometerChartOptions $options + * @return ezcGraphBoundings + */ + public function drawOdometer( + ezcGraphBoundings $boundings, + ezcGraphChartElementAxis $axis, + ezcGraphOdometerChartOptions $options ) + { + $height = $boundings->height * $options->odometerHeight; + + // Draw axis + $oldAxisSpace = $axis->axisSpace; + $axis->axisSpace = 0; + + $axis->render( $this, $boundings ); + + // Reset axisspaces to correct values + $this->xAxisSpace = $boundings->width * $oldAxisSpace; + $this->yAxisSpace = ( $boundings->height - $height ) / 2; + + $this->drawAxisLabels(); + + // Reduce size of chart boundings respecting requested odometer height + $boundings->x0 += $this->xAxisSpace; + $boundings->x1 -= $this->xAxisSpace; + $boundings->y0 += $this->yAxisSpace; + $boundings->y1 -= $this->yAxisSpace; + + $gradient = new ezcGraphLinearGradient( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + $options->startColor, + $options->endColor + ); + + // Simply draw box with gradient and optional border + $this->drawBox( + $boundings, + $gradient, + $options->borderColor, + $options->borderWidth + ); + + // Return modified chart boundings + return $boundings; + } + + /** + * Draw a single odometer marker. + * + * @param ezcGraphBoundings $boundings + * @param ezcGraphCoordinate $position + * @param int $symbol + * @param ezcGraphColor $color + * @param int $width + */ + public function drawOdometerMarker( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $position, + $symbol, + ezcGraphColor $color, + $width ) + { + $this->driver->drawLine( + new ezcGraphCoordinate( + $xPos = $boundings->x0 + ( $position->x * $boundings->width ), + $boundings->y0 + ), + new ezcGraphCoordinate( + $xPos, + $boundings->y1 + ), + $color, + $width + ); + } +} + +?> diff --git a/library/ezc/Graph/src/renderer/3d.php b/library/ezc/Graph/src/renderer/3d.php index 80b874c859..a630398f27 100644 --- a/library/ezc/Graph/src/renderer/3d.php +++ b/library/ezc/Graph/src/renderer/3d.php @@ -1,2424 +1,2424 @@ - - * $graph = new ezcGraphPieChart(); - * $graph->palette = new ezcGraphPaletteEzRed(); - * $graph->title = 'Access statistics'; - * $graph->options->label = '%2$d (%3$.1f%%)'; - * - * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( - * 'Mozilla' => 19113, - * 'Explorer' => 10917, - * 'Opera' => 1464, - * 'Safari' => 652, - * 'Konqueror' => 474, - * ) ); - * $graph->data['Access statistics']->highlight['Explorer'] = true; - * - * $graph->renderer = new ezcGraphRenderer3d(); - * - * $graph->renderer->options->moveOut = .2; - * - * $graph->renderer->options->pieChartOffset = 63; - * - * $graph->renderer->options->pieChartGleam = .3; - * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartShadowSize = 5; - * $graph->renderer->options->pieChartShadowColor = '#000000'; - * - * $graph->renderer->options->legendSymbolGleam = .5; - * $graph->renderer->options->legendSymbolGleamSize = .9; - * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; - * - * $graph->renderer->options->pieChartSymbolColor = '#55575388'; - * - * $graph->renderer->options->pieChartHeight = 5; - * $graph->renderer->options->pieChartRotation = .8; - * - * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' ); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphRenderer3d - extends - ezcGraphRenderer - implements - ezcGraphStackedBarsRenderer -{ - - /** - * Pie segment labels divided into two array, containing the labels on the - * left and right side of the pie chart center. - * - * @var array - */ - protected $pieSegmentLabels = array( - 0 => array(), - 1 => array(), - ); - - /** - * Contains the boundings used for pie segments - * - * @var ezcGraphBoundings - */ - protected $pieSegmentBoundings = false; - - /** - * Array with symbols for post processing, which ensures, that the symbols - * are rendered topmost. - * - * @var array - */ - protected $linePostSymbols = array(); - - /** - * Array containing lines from the axis and grid which should be redrawn on - * top of the data. - * - * @var array - */ - protected $frontLines = array(); - - /** - * Collects circle sectors to draw shadow in background of all circle - * sectors. - * - * @var array - */ - protected $circleSectors = array(); - - /** - * Collects bar sides to draw them in a post processing step to simulate - * a simple z buffer. - * array( - * array( - * 'index' => (int) // used for sorting - * 'context' => ezcGraphContext // context of call - * 'method' => (string) // method of driver to call - * 'parameters' => array // parameters for method call - * ), ... - * ) - * - * @var array - */ - protected $barPostProcessing = array(); - - /** - * Options - * - * @var ezcGraphRenderer3dOptions - */ - protected $options; - - /** - * Depth of displayed pseudo three dimensional line chart elements. - * - * @var float - */ - protected $depth = false; - - /** - * Factor to reduce the width according to depth - * - * @var float - */ - protected $xDepthFactor = false; - - /** - * Factor to reduce the height according to depth - * - * @var float - */ - protected $yDepthFactor = false; - - /** - * Boundings for the chart data - * - * @var ezcGraphBoundings - */ - protected $dataBoundings = false; - - /** - * Collect axis labels, so that the axis are drawn, when all axis spaces - * are known. - * - * @var array - */ - protected $axisLabels = array(); - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->options = new ezcGraphRenderer3dOptions( $options ); - } - - /** - * __get - * - * @param mixed $propertyName - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return mixed - * @ignore - */ - public function __get( $propertyName ) - { - switch ( $propertyName ) - { - case 'options': - return $this->options; - default: - return parent::__get( $propertyName ); - } - } - - /** - * Calculate the display coordinate from a coordinate - * - * Calculates the display coordinate of a coordinate depending on the - * depth setting and the distance of the coordinate to the front of the - * chart. - * - * @param ezcGraphCoordinate $c Coordinate - * @param float $front Distance to front (0 - 1) - * @return ezcGraphCoordinate Resulting coordinate - */ - protected function get3dCoordinate( ezcGraphCoordinate $c, $front = 1. ) - { - return new ezcGraphCoordinate( - ( $c->x - $this->dataBoundings->x0 ) * $this->xDepthFactor + $this->dataBoundings->x0 + $this->depth * $front, - ( $c->y - $this->dataBoundings->y0 ) * $this->yDepthFactor + $this->dataBoundings->y0 + $this->depth * ( 1 - $front ) - ); - } - - /** - * Draw pie segment - * - * Draws a single pie segment - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of pie segment - * @param float $startAngle Start angle - * @param float $endAngle End angle - * @param mixed $label Label of pie segment - * @param bool $moveOut Move out from middle for hilighting - * @return void - */ - public function drawPieSegment( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - $startAngle = .0, - $endAngle = 360., - $label = false, - $moveOut = false ) - { - // Apply offset - $startAngle += $this->options->pieChartOffset; - $endAngle += $this->options->pieChartOffset; - - // Calculate position and size of pie - $center = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - - $this->options->pieChartHeight / 2 - ); - - // Limit radius to fourth of width and half of height at maximum - $radius = min( - ( $boundings->x1 - $boundings->x0 ) * $this->options->pieHorizontalSize, - ( $boundings->y1 - $boundings->y0 ) * $this->options->pieVerticalSize - ); - - // Move pie segment out of the center - if ( $moveOut ) - { - $direction = ( $endAngle + $startAngle ) / 2; - - $center = new ezcGraphCoordinate( - $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ), - $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) * $this->options->pieChartRotation - ); - } - - // Add circle sector to queue - $this->circleSectors[] = array( - 'center' => $center, - 'context' => $context, - 'width' => $radius * 2 * ( 1 - $this->options->moveOut ), - 'height' => $radius * 2 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation - $this->options->pieChartHeight, - 'start' => $startAngle, - 'end' => $endAngle, - 'color' => $color, - ); - - if ( $label ) - { - // Determine position of label - $direction = ( $endAngle + $startAngle ) / 2; - $pieSegmentCenter = new ezcGraphCoordinate( - $center->x + cos( deg2rad( $direction ) ) * $radius, - $center->y + sin( deg2rad( $direction ) ) * $radius * $this->options->pieChartRotation - ); - - // Split labels up into left a right site and index them on their - // y position - $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][(int) ( $pieSegmentCenter->y * 100 )] = array( - new ezcGraphCoordinate( - $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3 * ( 1 - $this->options->moveOut ), - $center->y + sin( deg2rad( $direction ) ) * ( $radius - $this->options->pieChartHeight ) * 2 / 3 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation - ), - $label, - $context, - ); - } - - if ( !$this->pieSegmentBoundings ) - { - $this->pieSegmentBoundings = $boundings; - } - } - - /** - * Draws the collected pie segment labels - * - * All labels are collected and drawn later to be able to partition the - * available space for the labels woth knowledge of the overall label - * count and their required size and optimal position. - * - * @return void - */ - protected function finishPieSegmentLabels() - { - if ( $this->pieSegmentBoundings === false ) - { - return true; - } - - $boundings = $this->pieSegmentBoundings; - - // Calculate position and size of pie - $center = new ezcGraphCoordinate( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, - $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 - ); - - // Limit radius to fourth of width and half of height at maximum - $radius = min( - ( $boundings->width ) * $this->options->pieHorizontalSize, - ( $boundings->height ) * $this->options->pieVerticalSize - ); - - $pieChartHeight = min( - $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4, - $boundings->height - ); - $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2; - - // Calculate maximum height of labels - $labelHeight = min( - ( count( $this->pieSegmentLabels[0] ) - ? $pieChartHeight / count( $this->pieSegmentLabels[0] ) - : $pieChartHeight - ), - ( count( $this->pieSegmentLabels[1] ) - ? $pieChartHeight / count( $this->pieSegmentLabels[1] ) - : $pieChartHeight - ), - ( $pieChartHeight ) * $this->options->maxLabelHeight - ); - - $symbolSize = $this->options->symbolSize; - - foreach ( $this->pieSegmentLabels as $side => $labelPart ) - { - $minHeight = $pieChartYPosition; - $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight; - - // Sort to draw topmost label first - ksort( $labelPart ); - $sign = ( $side ? -1 : 1 ); - - foreach ( $labelPart as $height => $label ) - { - $height = (int) ( $height / 100 ); - - if ( ( $height - $labelHeight / 2 ) > $minHeight ) - { - $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight ); - $minHeight += $share; - $toShare -= $share; - } - - // Determine position of label - $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare; - $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius; - - $labelPosition = new ezcGraphCoordinate( - $center->x - - $sign * ( - abs( $verticalDistance ) > 1 - // If vertical distance to center is greater then the - // radius, use the centerline for the horizontal - // position - ? max ( - 5, - abs( $label[0]->x - $center->x ) - ) - // Else place the label outside of the pie chart - : ( cos ( asin ( $verticalDistance ) ) * $radius + - $symbolSize * (int) $this->options->showSymbol - ) - ), - $minHeight + $labelHeight / 2 - ); - - if ( $this->options->showSymbol ) - { - // Draw label - $this->driver->drawLine( - $label[0], - $labelPosition, - $this->options->pieChartSymbolColor, - 1 - ); - - $this->driver->drawCircle( - $label[0], - $symbolSize, - $symbolSize, - $this->options->pieChartSymbolColor, - true - ); - $this->driver->drawCircle( - $labelPosition, - $symbolSize, - $symbolSize, - $this->options->pieChartSymbolColor, - true - ); - } - - $this->addElementReference( $label[2], - $this->driver->drawTextBox( - $label[1], - new ezcGraphCoordinate( - ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ), - $minHeight - ), - ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ), - $labelHeight, - ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE - ) - ); - - // Add used space to minHeight - $minHeight += $labelHeight; - } - } - } - - /** - * Draws the collected circle sectors - * - * All circle sectors are collected and drawn later to be able to render - * the shadows of the pie segments in the back of all pie segments, and - * ensure the correct drawing order for all pie segment elements. - * - * @return void - */ - protected function finishCirleSectors() - { - $zBuffer = array(); - - $shadows = array(); - $shadowCenter = false; - $shadowEndAngle = false; - - // Add circle sector sides to simple z buffer prioriry list - foreach ( $this->circleSectors as $circleSector ) - { - // Draw shadow if wanted - if ( $this->options->pieChartShadowSize > 0 ) - { - if ( $shadowEndAngle === false ) - { - $shadowStartAngle = $circleSector['start']; - $shadowEndAngle = $circleSector['end']; - $shadowCenter = $circleSector['center']; - } - elseif ( $circleSector['center'] == $shadowCenter ) - { - $shadowEndAngle = $circleSector['end']; - } - else - { - $shadows[] = array( - 'center' => $shadowCenter, - 'start' => $shadowStartAngle, - 'end' => $shadowEndAngle, - 'width' => $circleSector['width'], - 'height' => $circleSector['height'], - ); - - $shadowCenter = $circleSector['center']; - $shadowStartAngle = $circleSector['start']; - $shadowEndAngle = $circleSector['end']; - } - } - - $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); - - $center = (int) ( $circleSector['center']->y + sin( deg2rad( $circleSector['start'] + ( $circleSector['end'] - $circleSector['start'] ) / 2 ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight / 2 + 1 ); - - $zBuffer[$center][] = array( - 'method' => 'drawCircularArc', - 'paramenters' => array( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - $this->options->pieChartHeight, - $circleSector['start'], - $circleSector['end'], - $circleSector['color'] - ) - ); - - // Left side - $polygonPoints = array( - $circleSector['center'], - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $this->options->pieChartHeight - ), - new ezcGraphCoordinate( - $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, - $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight - ), - new ezcGraphCoordinate( - $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, - $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 - ), - ); - - // Get average y coordinate for polygon to use for zBuffer - $center = 0; - foreach ( $polygonPoints as $point ) - { - $center += $point->y; - } - $center = (int) ( $center / count( $polygonPoints ) ); - - $zBuffer[$center][] = array( - 'method' => 'drawPolygon', - 'paramenters' => array( - $polygonPoints, - $circleSector['color'], - true - ), - ); - - $zBuffer[$center][] = array( - 'method' => 'drawPolygon', - 'paramenters' => array( - $polygonPoints, - $darkenedColor, - false - ), - ); - - // Right side - $polygonPoints = array( - $circleSector['center'], - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $this->options->pieChartHeight - ), - new ezcGraphCoordinate( - $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, - $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight - ), - new ezcGraphCoordinate( - $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, - $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 - ), - ); - - // Get average y coordinate for polygon to use for zBuffer - $center = 0; - foreach ( $polygonPoints as $point ) - { - $center += $point->y; - } - $center = (int) ( $center / count( $polygonPoints ) ); - - $zBuffer[$center][] = array( - 'method' => 'drawPolygon', - 'paramenters' => array( - $polygonPoints, - $circleSector['color'], - true - ), - ); - - $zBuffer[$center][] = array( - 'method' => 'drawPolygon', - 'paramenters' => array( - $polygonPoints, - $darkenedColor, - false - ), - ); - } - - if ( $this->options->pieChartShadowSize > 0 ) - { - $shadows[] = array( - 'center' => $shadowCenter, - 'start' => $shadowStartAngle, - 'end' => $shadowEndAngle, - 'width' => $circleSector['width'], - 'height' => $circleSector['height'], - ); - } - - // Draw collected shadows - foreach ( $shadows as $circleSector ) - { - for ( $i = $this->options->pieChartShadowSize; $i > 0; --$i ) - { - $startAngle = $circleSector['start']; - $endAngle = $circleSector['end']; - - $startAngle = $circleSector['start'] - ( $this->options->pieChartShadowSize - $i ); - $endAngle = $circleSector['end'] + ( $this->options->pieChartShadowSize - $i ); - - if ( ( $endAngle - $startAngle ) >= 360 ) - { - $this->driver->drawCircle( - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $this->options->pieChartHeight - ), - $circleSector['width'] + $i * 2, - $circleSector['height'] + $i * 2, - $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), - true - ); - } - else - { - $this->driver->drawCircleSector( - new ezcGraphCoordinate( - $circleSector['center']->x, - $circleSector['center']->y + $this->options->pieChartHeight - ), - $circleSector['width'] + $i * 2, - $circleSector['height'] + $i * 2, - $startAngle, - $endAngle, - $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), - true - ); - } - } - } - - ksort( $zBuffer ); - foreach ( $zBuffer as $sides ) - { - foreach ( $sides as $side ) - { - call_user_func_array( array( $this->driver, $side['method'] ), $side['paramenters'] ); - } - } - - // Draw circle sector for front - foreach ( $this->circleSectors as $circleSector ) - { - $this->addElementReference( $circleSector['context'], - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - $circleSector['start'], - $circleSector['end'], - $circleSector['color'], - true - ) - ); - - if ( $this->options->pieChartGleam !== false ) - { - $gradient = new ezcGraphLinearGradient( - $circleSector['center'], - new ezcGraphCoordinate( - $circleSector['center']->x - $circleSector['width'] / 2, - $circleSector['center']->y - $circleSector['height'] / 2 - ), - $this->options->pieChartGleamColor->transparent( 1 ), - $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) - ); - - $this->addElementReference( $circleSector['context'], - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'] - $this->options->pieChartGleamBorder * 2, - $circleSector['height'] - $this->options->pieChartGleamBorder * 2 * $this->options->pieChartRotation, - $circleSector['start'], - $circleSector['end'], - $gradient, - true - ) - ); - } - - $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); - $this->driver->drawCircleSector( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - $circleSector['start'], - $circleSector['end'], - $darkenedColor, - false - ); - - if ( $this->options->pieChartGleam !== false ) - { - $radialGradient = new ezcGraphRadialGradient( - new ezcGraphCoordinate( - $circleSector['center']->x + $circleSector['width'] / 2 * cos( deg2rad( 135 ) ), - $circleSector['center']->y + $circleSector['height'] / 2 * sin( deg2rad( 135 ) ) - ), - $circleSector['width'], - $circleSector['height'], - $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ), - $this->options->pieChartGleamColor->transparent( .8 ) - ); - - $this->driver->drawCircularArc( - $circleSector['center'], - $circleSector['width'], - $circleSector['height'], - 0, - $circleSector['start'], - $circleSector['end'], - $radialGradient, - false - ); - } - } - } - - /** - * Draw collected front lines - * - * Draw all grid and axis lines, which should be redrawn in front of the - * data. - * - * @return void - */ - protected function finishFrontLines() - { - foreach ( $this->frontLines as $line ) - { - $this->driver->drawLine( - $line[0], - $line[1], - $line[2], - $line[3] - ); - } - } - - /** - * Draw the collected line symbols - * - * Symbols for the data lines are collected and delayed to ensure that - * they are not covered and hidden by other data lines. - * - * @return void - */ - protected function finishLineSymbols() - { - foreach ( $this->linePostSymbols as $symbol ) - { - $this->addElementReference( $symbol['context'], - $this->drawSymbol( - $symbol['boundings'], - $symbol['color'], - $symbol['symbol'] - ) - ); - } - } - - /** - * Draws a bar with a rectangular ground shape. - * - * @param ezcGraphContext $context - * @param ezcGraphColor $color - * @param ezcGraphCoordinate $position - * @param float $barWidth - * @param float $offset - * @param float $axisPosition - * @param float $startDepth - * @param float $midDepth - * @param float $endDepth - * @return void - */ - protected function drawRectangularBar( - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth ) - { - $barPolygonArray = array( - new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ), - new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, - $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ), - new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, - $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ), - new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ), - ); - - // Draw right bar side - $this->barPostProcessing[] = array( - 'index' => $barPolygonArray[2]->x + ( 1 - $position->y ), - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), - ), - $color->darken( $this->options->barDarkenSide ), - true - ), - ); - - // Draw top side - $this->barPostProcessing[] = array( - 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - ( $barPolygonArray[1]->y < $barPolygonArray[3]->y - ? array( - $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), - $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), - ) - : array( - $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), - $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), - ) - ), - $color->darken( $this->options->barDarkenTop ), - true - ), - ); - - // Draw top side gleam - if ( $this->options->barChartGleam !== false ) - { - $this->barPostProcessing[] = array( - 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - ( $barPolygonArray[1]->y < $barPolygonArray[3]->y - ? array( - $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), - $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), - ) - : array( - $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), - $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), - ) - ), - new ezcGraphLinearGradient( - ( $barPolygonArray[1]->y < $barPolygonArray[3]->y - ? $this->get3dCoordinate( $barPolygonArray[2], $endDepth ) - : $this->get3dCoordinate( $barPolygonArray[3], $endDepth ) - ), - ( $barPolygonArray[1]->y < $barPolygonArray[3]->y - ? $this->get3dCoordinate( $barPolygonArray[1], $startDepth ) - : $this->get3dCoordinate( $barPolygonArray[0], $startDepth ) - ), - ezcGraphColor::fromHex( '#FFFFFFFF' ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) - ), - true - ), - ); - } - - // Draw front side - $this->barPostProcessing[] = array( - 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - ), - $color, - true - ), - ); - - // Draw front side gleam - if ( $this->options->barChartGleam !== false ) - { - $this->barPostProcessing[] = array( - 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - ), - new ezcGraphLinearGradient( - $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), - $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), - ezcGraphColor::fromHex( '#FFFFFFFF' ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) - ), - true - ), - ); - } - } - - /** - * Draws a bar with a diamond ground shape. - * - * @param ezcGraphContext $context - * @param ezcGraphColor $color - * @param ezcGraphCoordinate $position - * @param float $barWidth - * @param float $offset - * @param float $axisPosition - * @param float $startDepth - * @param float $midDepth - * @param float $endDepth - * @return void - */ - protected function drawDiamondBar( - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth ) - { - $barCoordinateArray = array( - // The bottom point of the diamond is moved to .7 instead - // of .5 because it looks more correct, even it is wrong... - 'x' => array( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .7, - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .3, - ), - 'y' => array( - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), - ), - ); - - // Left side - $this->barPostProcessing[] = array( - 'index' => $barCoordinateArray['x'][0], - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][0] ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][1] ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), - ), - $color, - true - ), - ); - - // Right side - $this->barPostProcessing[] = array( - 'index' => $barCoordinateArray['x'][1], - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][0] ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][1] ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), - ), - $color->darken( $this->options->barDarkenSide ), - true - ), - ); - - $topLocation = min( - $barCoordinateArray['y'][0], - $barCoordinateArray['y'][1] - ); - - // Top side - $this->barPostProcessing[] = array( - 'index' => $barCoordinateArray['x'][0], - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), - ), - $color->darken( $this->options->barDarkenTop ), - true - ), - ); - - // Top side gleam - if ( $this->options->barChartGleam !== false ) - { - $this->barPostProcessing[] = array( - 'index' => $barCoordinateArray['x'][0] + 1, - 'method' => 'drawPolygon', - 'context' => $context, - 'parameters' => array( - array( - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), - ), - new ezcGraphLinearGradient( - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), - $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), - ezcGraphColor::fromHex( '#FFFFFFFF' ), - ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) - ), - true - ), - ); - } - } - - /** - * Draws a bar with a circular ground shape. - * - * @param ezcGraphContext $context - * @param ezcGraphColor $color - * @param ezcGraphCoordinate $position - * @param float $barWidth - * @param float $offset - * @param float $axisPosition - * @param float $startDepth - * @param float $midDepth - * @param float $endDepth - * @param int $symbol - * @return void - */ - protected function drawCircularBar( - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth, - $symbol ) - { - $barCenterTop = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, - $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - - ); - $barCenterBottom = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - - if ( $barCenterTop->y > $barCenterBottom->y ) - { - $tmp = $barCenterTop; - $barCenterTop = $barCenterBottom; - $barCenterBottom = $tmp; - } - - $this->barPostProcessing[] = array( - 'index' => $barCenterBottom->x, - 'method' => 'drawCircularArc', - 'context' => $context, - 'parameters' => array( - $this->get3dCoordinate( $barCenterTop, $midDepth ), - $barWidth, - $barWidth / 2, - ( $barCenterBottom->y - $barCenterTop->y ) * $this->yDepthFactor, - 0, - 180, - $color - ), - ); - - $this->barPostProcessing[] = array( - 'index' => $barCenterBottom->x + 1, - 'method' => 'drawCircle', - 'context' => $context, - 'parameters' => array( - $top = $this->get3dCoordinate( $barCenterTop, $midDepth ), - $barWidth, - $barWidth / 2, - ( $symbol === ezcGraph::CIRCLE - ? new ezcGraphLinearGradient( - new ezcGraphCoordinate( - $top->x - $barWidth / 2, - $top->y - ), - new ezcGraphCoordinate( - $top->x + $barWidth / 2, - $top->y - ), - $color->darken( $this->options->barDarkenTop ), - $color - ) - : $color - ) - ), - ); - } - - /** - * Draw bar - * - * Draws a bar as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $position Position of data point - * @param float $stepSize Space which can be used for bars - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param float $axisPosition Position of axis for drawing filled lines - * @return void - */ - public function drawBar( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $position, - $stepSize, - $dataNumber = 1, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - $axisPosition = 0. ) - { - // Apply margin - $margin = $stepSize * $this->options->barMargin; - $padding = $stepSize * $this->options->barPadding; - $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; - $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2; - - if ( $barWidth < 0 ) - { - $offset -= $barWidth = abs( $barWidth ); - } - - $startDepth = $this->options->barMargin; - $midDepth = .5; - $endDepth = 1 - $this->options->barMargin; - - switch ( $symbol ) - { - case ezcGraph::NO_SYMBOL: - $this->drawRectangularBar( - $context, - $color, - $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth - ); - break; - case ezcGraph::DIAMOND: - $this->drawDiamondBar( - $context, - $color, - $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth - ); - break; - case ezcGraph::BULLET: - case ezcGraph::CIRCLE: - $this->drawCircularBar( - $context, - $color, - $position, - $barWidth, - $offset, - $axisPosition, - $startDepth, - $midDepth, - $endDepth, - $symbol - ); - break; - } - } - - /** - * Draw stacked bar - * - * Draws a stacked bar part as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $start - * @param ezcGraphCoordinate $position - * @param float $stepSize Space which can be used for bars - * @param int $symbol Symbol to draw for line - * @param float $axisPosition Position of axis for drawing filled lines - * @return void - */ - public function drawStackedBar( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $start, - ezcGraphCoordinate $position, - $stepSize, - $symbol = ezcGraph::NO_SYMBOL, - $axisPosition = 0. ) - { - // Apply margin - $margin = $stepSize * $this->options->barMargin; - $barWidth = $stepSize - $margin; - $offset = - $stepSize / 2 + $margin / 2; - - if ( $barWidth < 0 ) - { - $offset -= $barWidth = abs( $barWidth ); - } - - $startDepth = $this->options->barMargin; - $midDepth = .5; - $endDepth = 1 - $this->options->barMargin; - - switch ( $symbol ) - { - case ezcGraph::NO_SYMBOL: - case ezcGraph::DIAMOND: - case ezcGraph::BULLET: - case ezcGraph::CIRCLE: - $this->drawRectangularBar( - $context, - $color, - $position, - $barWidth, - $offset, - $start->y, - $startDepth, - $midDepth, - $endDepth - ); - break; - } - } - - /** - * Draw all collected bar elements - * - * Draw all collected bar elements after sorting them depending of their - * position to simulate simple z buffering. - * - * @access protected - * @return void - */ - protected function finishBars() - { - if ( !count( $this->barPostProcessing ) ) - { - return true; - } - - $zIndexArray = array(); - foreach ( $this->barPostProcessing as $key => $barPolygon ) - { - $zIndexArray[$key] = $barPolygon['index']; - } - - array_multisort( - $zIndexArray, SORT_ASC, SORT_NUMERIC, - $this->barPostProcessing - ); - - foreach ( $this->barPostProcessing as $bar ) - { - $this->addElementReference( $bar['context'], - call_user_func_array( - array( $this->driver, $bar['method'] ), - $bar['parameters'] - ) - ); - } - } - - /** - * Draw data line - * - * Draws a line as a data element in a line chart - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphColor $color Color of line - * @param ezcGraphCoordinate $start Starting point - * @param ezcGraphCoordinate $end Ending point - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param int $symbol Symbol to draw for line - * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor - * @param ezcGraphColor $fillColor Color to fill line with - * @param float $axisPosition Position of axis for drawing filled lines - * @param float $thickness Line thickness - * @return void - */ - public function drawDataLine( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphColor $color, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - $dataNumber = 0, - $dataCount = 1, - $symbol = ezcGraph::NO_SYMBOL, - ezcGraphColor $symbolColor = null, - ezcGraphColor $fillColor = null, - $axisPosition = 0., - $thickness = 1. ) - { - // Calculate line width based on options - if ( $this->options->seperateLines ) - { - $startDepth = ( 1 / $dataCount ) * $dataNumber; - $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); - } - else - { - $startDepth = false; - $endDepth = true; - } - - // Determine Coordinates depending on boundings and data point position - $startCoord = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $start->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - $endCoord = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - - // 3D-fy coordinates - $linePolygonPoints = array( - $this->get3dCoordinate( $startCoord, $startDepth ), - $this->get3dCoordinate( $endCoord, $startDepth ), - $this->get3dCoordinate( $endCoord, $endDepth ), - $this->get3dCoordinate( $startCoord, $endDepth ), - ); - - $startAxisCoord = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - $endAxisCoord = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - - // 3D-fy coordinates - $axisPolygonPoints = array( - $this->get3dCoordinate( $startAxisCoord, $startDepth ), - $this->get3dCoordinate( $endAxisCoord, $startDepth ), - $this->get3dCoordinate( $endAxisCoord, $endDepth ), - $this->get3dCoordinate( $startAxisCoord, $endDepth ), - ); - - // Perhaps fill up line - if ( $fillColor !== null && - $start->x != $end->x ) - { - $startValue = $axisPosition - $start->y; - $endValue = $axisPosition - $end->y; - - if ( ( $startValue == 0 ) || - ( $endValue == 0 ) || - ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) ) - { - // Values have the same sign or are on the axis - $this->driver->drawPolygon( - array( - $linePolygonPoints[0], - $linePolygonPoints[1], - $this->get3dCoordinate( $endAxisCoord, $startDepth ), - $this->get3dCoordinate( $startAxisCoord, $startDepth ), - ), - $fillColor, - true - ); - } - else - { - // values are on differente sides of the axis - split the filled polygon - $startDiff = abs( $axisPosition - $start->y ); - $endDiff = abs( $axisPosition - $end->y ); - - $cuttingPosition = $startDiff / ( $endDiff + $startDiff ); - $cuttingPoint = new ezcGraphCoordinate( - $startCoord->x + ( $endCoord->x - $startCoord->x ) * $cuttingPosition, - $startAxisCoord->y - ); - - $this->driver->drawPolygon( - array( - $this->get3dCoordinate( $startAxisCoord, $startDepth ), - $linePolygonPoints[0], - $this->get3dCoordinate( $cuttingPoint, $startDepth ), - ), - $fillColor, - true - ); - - $this->driver->drawPolygon( - array( - $this->get3dCoordinate( $endAxisCoord, $startDepth ), - $linePolygonPoints[1], - $this->get3dCoordinate( $cuttingPoint, $startDepth ), - ), - $fillColor, - true - ); - } - - // Draw closing foo - $this->driver->drawPolygon( - array( - $linePolygonPoints[2], - $linePolygonPoints[1], - $this->get3dCoordinate( $endAxisCoord, $startDepth ), - $this->get3dCoordinate( $endAxisCoord, $endDepth ), - ), - $fillColor, - true - ); - } - - - // Draw line - $this->driver->drawPolygon( - $linePolygonPoints, - $color, - true, - $thickness - ); - - // Draw polygon border - if ( $this->options->dataBorder > 0 ) - { - $this->driver->drawPolygon( - $linePolygonPoints, - $color->darken( $this->options->dataBorder ), - false, - $thickness - ); - } - - // Draw line symbol - if ( $this->options->showSymbol && - ( $symbol !== ezcGraph::NO_SYMBOL ) ) - { - if ( $symbolColor === null ) - { - $symbolColor = $color; - } - - $this->linePostSymbols[] = array( - 'boundings' => new ezcGraphBoundings( - $linePolygonPoints[2]->x - $this->options->symbolSize / 2, - $linePolygonPoints[2]->y - $this->options->symbolSize / 2, - $linePolygonPoints[2]->x + $this->options->symbolSize / 2, - $linePolygonPoints[2]->y + $this->options->symbolSize / 2 - ), - 'color' => $symbolColor, - 'context' => $context, - 'symbol' => $symbol, - ); - } - } - - /** - * Draws a highlight textbox for a datapoint. - * - * A highlight textbox for line and bar charts means a box with the current - * value in the graph. - * - * @param ezcGraphBoundings $boundings Chart boundings - * @param ezcGraphContext $context Context of call - * @param ezcGraphCoordinate $end Ending point - * @param float $axisPosition Position of axis for drawing filled lines - * @param int $dataNumber Number of dataset - * @param int $dataCount Count of datasets in chart - * @param ezcGraphFontOptions $font Font used for highlight string - * @param string $text Acutual value - * @param int $size Size of highlight text - * @param ezcGraphColor $markLines - * @param int $xOffset - * @param int $yOffset - * @param float $stepSize - * @param int $type - * @return void - */ - public function drawDataHighlightText( - ezcGraphBoundings $boundings, - ezcGraphContext $context, - ezcGraphCoordinate $end, - $axisPosition = 0., - $dataNumber = 1, - $dataCount = 1, - ezcGraphFontOptions $font, - $text, - $size, - ezcGraphColor $markLines = null, - $xOffset = 0, - $yOffset = 0, - $stepSize = 0., - $type = ezcGraph::LINE ) - { - $this->driver->options->font = $font; - $width = $this->dataBoundings->width / $dataCount; - - // Calculate line width based on options - if ( $this->options->seperateLines ) - { - $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); - } - else - { - $endDepth = true; - } - - $dataPoint = new ezcGraphCoordinate( - $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), - $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) - ); - - if ( $end->y < $axisPosition ) - { - $this->driver->drawTextBox( - $text, - $this->get3dCoordinate( new ezcGraphCoordinate( - $dataPoint->x - $width / 2, - $dataPoint->y - $size - $font->padding - $this->options->symbolSize - ), $endDepth ), - $width * $this->xDepthFactor, - $size, - ezcGraph::CENTER | ezcGraph::BOTTOM - ); - } - else - { - $this->driver->drawTextBox( - $text, - $this->get3dCoordinate( new ezcGraphCoordinate( - $dataPoint->x - $width / 2, - $dataPoint->y + $font->padding + $this->options->symbolSize - ), $endDepth ), - $width * $this->xDepthFactor, - $size, - ezcGraph::CENTER | ezcGraph::TOP - ); - } - } - - /** - * Draw legend - * - * Will draw a legend in the bounding box - * - * @param ezcGraphBoundings $boundings Bounding of legend - * @param ezcGraphChartElementLegend $legend Legend to draw; - * @param int $type Type of legend: Protrait or landscape - * @return void - */ - public function drawLegend( - ezcGraphBoundings $boundings, - ezcGraphChartElementLegend $legend, - $type = ezcGraph::VERTICAL ) - { - $labels = $legend->labels; - - // Calculate boundings of each label - if ( $type & ezcGraph::VERTICAL ) - { - $labelWidth = $boundings->x1 - $boundings->x0; - $labelHeight = min( - ( $boundings->y1 - $boundings->y0 ) / count( $labels ) - $legend->spacing, - $legend->symbolSize + 2 * $legend->padding - ); - } - else - { - $labelWidth = ( $boundings->x1 - $boundings->x0 ) / count( $labels ) - $legend->spacing; - $labelHeight = min( - $boundings->height, - $legend->symbolSize + 2 * $legend->padding - ); - } - - $symbolSize = $labelHeight - 2 * $legend->padding; - - // Draw all labels - $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ); - foreach ( $labels as $label ) - { - $this->elements['legend_url'][$label['label']] = $label['url']; - - $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol( - new ezcGraphBoundings( - $labelPosition->x + $legend->padding, - $labelPosition->y + $legend->padding, - $labelPosition->x + $legend->padding + $symbolSize, - $labelPosition->y + $legend->padding + $symbolSize - ), - $label['color'], - $label['symbol'] - ); - - $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox( - $label['label'], - new ezcGraphCoordinate( - $labelPosition->x + 2 * $legend->padding + $symbolSize, - $labelPosition->y + $legend->padding - ), - $labelWidth - $symbolSize - 3 * $legend->padding, - $labelHeight - 2 * $legend->padding, - ezcGraph::LEFT | ezcGraph::MIDDLE - ); - - $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing ); - $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 ); - } - } - - /** - * Draw box - * - * Box are wrapping each major chart element and draw border, background - * and title to each chart element. - * - * Optionally a padding and margin for each box can be defined. - * - * @param ezcGraphBoundings $boundings Boundings of the box - * @param ezcGraphColor $background Background color - * @param ezcGraphColor $borderColor Border color - * @param int $borderWidth Border width - * @param int $margin Margin - * @param int $padding Padding - * @param mixed $title Title of the box - * @param int $titleSize Size of title in the box - * @return ezcGraphBoundings Remaining inner boundings - */ - public function drawBox( - ezcGraphBoundings $boundings, - ezcGraphColor $background = null, - ezcGraphColor $borderColor = null, - $borderWidth = 0, - $margin = 0, - $padding = 0, - $title = false, - $titleSize = 16 ) - { - // Apply margin - $boundings->x0 += $margin; - $boundings->y0 += $margin; - $boundings->x1 -= $margin; - $boundings->y1 -= $margin; - - if ( $background instanceof ezcGraphColor ) - { - // Draw box background - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $background, - true - ); - } - - if ( ( $borderColor instanceof ezcGraphColor ) && - ( $borderWidth > 0 ) ) - { - // Draw border - $this->driver->drawPolygon( - array( - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), - new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), - ), - $borderColor, - false, - $borderWidth - ); - - // Reduce local boundings by borderWidth - $boundings->x0 += $borderWidth; - $boundings->y0 += $borderWidth; - $boundings->x1 -= $borderWidth; - $boundings->y1 -= $borderWidth; - } - - // Apply padding - $boundings->x0 += $padding; - $boundings->y0 += $padding; - $boundings->x1 -= $padding; - $boundings->y1 -= $padding; - - // Add box title - if ( $title !== false ) - { - switch ( $this->options->titlePosition ) - { - case ezcGraph::TOP: - $this->driver->drawTextBox( - $title, - new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), - $boundings->x1 - $boundings->x0, - $titleSize, - $this->options->titleAlignement - ); - - $boundings->y0 += $titleSize + $padding; - $boundings->y1 -= $titleSize + $padding; - break; - case ezcGraph::BOTTOM: - $this->driver->drawTextBox( - $title, - new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ), - $boundings->x1 - $boundings->x0, - $titleSize, - $this->options->titleAlignement - ); - - $boundings->y1 -= $titleSize + $padding; - break; - } - } - - return $boundings; - } - - /** - * Draw text - * - * Draws the provided text in the boundings - * - * @param ezcGraphBoundings $boundings Boundings of text - * @param string $text Text - * @param int $align Alignement of text - * @param ezcGraphRotation $rotation - * @return void - */ - public function drawText( - ezcGraphBoundings $boundings, - $text, - $align = ezcGraph::LEFT, - ezcGraphRotation $rotation = null ) - { - if ( $this->depth === false ) - { - // We are not 3d for now, wg. rendering normal text boxes like the - // title - $topleft = new ezcGraphCoordinate( - $boundings->x0, - $boundings->y0 - ); - $bottomright = new ezcGraphCoordinate( - $boundings->x1, - $boundings->y1 - ); - } - else - { - // The 3d part started - $topleft = $this->get3dCoordinate( - new ezcGraphCoordinate( - $boundings->x0, - $boundings->y0 - ), false - ); - $bottomright = $this->get3dCoordinate( - new ezcGraphCoordinate( - $boundings->x1, - $boundings->y1 - ), false - ); - - // Also modify rotation accordingly - if ( $rotation !== null ) - { - $rotation = new ezcGraphRotation( - $rotation->getRotation(), - $this->get3dCoordinate( $rotation->getCenter(), false ) - ); - } - } - - $this->driver->drawTextBox( - $text, - $topleft, - $bottomright->x - $topleft->x, - $bottomright->y - $topleft->y, - $align, - $rotation - ); - } - - /** - * Draw grid line - * - * Draw line for the grid in the chart background - * - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Color of the grid line - * @return void - */ - public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) - { - $gridPolygonCoordinates = array( - $this->get3dCoordinate( $start, false ), - $this->get3dCoordinate( $end, false ), - $this->get3dCoordinate( $end, true ), - $this->get3dCoordinate( $start, true ), - ); - - // Draw grid polygon - if ( $this->options->fillGrid === 0 ) - { - $this->driver->drawLine( - $gridPolygonCoordinates[2], - $gridPolygonCoordinates[3], - $color - ); - } - else - { - if ( $this->options->fillGrid === 1 ) - { - $this->driver->drawPolygon( - $gridPolygonCoordinates, - $color, - true - ); - } - else - { - $this->driver->drawPolygon( - $gridPolygonCoordinates, - $color->transparent( $this->options->fillGrid ), - true - ); - } - - // Draw grid lines - scedule some for later to be drawn in front of - // the data - $this->frontLines[] = array( - $gridPolygonCoordinates[0], - $gridPolygonCoordinates[1], - $color, - 1 - ); - - $this->frontLines[] = array( - $gridPolygonCoordinates[1], - $gridPolygonCoordinates[2], - $color, - 1 - ); - - $this->driver->drawLine( - $gridPolygonCoordinates[2], - $gridPolygonCoordinates[3], - $color, - 1 - ); - - $this->frontLines[] = array( - $gridPolygonCoordinates[3], - $gridPolygonCoordinates[0], - $color, - 1 - ); - } - } - - /** - * Draw step line - * - * Draw a step (marker for label position) on a axis. - * - * @param ezcGraphCoordinate $start Start point - * @param ezcGraphCoordinate $end End point - * @param ezcGraphColor $color Color of the grid line - * @return void - */ - public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) - { - $stepPolygonCoordinates = array( - $this->get3dCoordinate( $start, true ), - $this->get3dCoordinate( $end, true ), - $this->get3dCoordinate( $end, false ), - $this->get3dCoordinate( $start, false ), - ); - - // Draw step polygon - if ( ( $this->options->fillAxis > 0 ) && - ( $this->options->fillAxis < 1 ) ) - { - $this->driver->drawPolygon( - $stepPolygonCoordinates, - $color->transparent( $this->options->fillAxis ), - true - ); - - $this->driver->drawPolygon( - $stepPolygonCoordinates, - $color, - false - ); - } - else - { - $this->driver->drawPolygon( - $stepPolygonCoordinates, - $color, - ! (bool) $this->options->fillAxis - ); - } - } - - /** - * Draw axis - * - * Draws an axis form the provided start point to the end point. A specific - * angle of the axis is not required. - * - * For the labeleing of the axis a sorted array with major steps and an - * array with minor steps is expected, which are build like this: - * array( - * array( - * 'position' => (float), - * 'label' => (string), - * ) - * ) - * where the label is optional. - * - * The label renderer class defines how the labels are rendered. For more - * documentation on this topic have a look at the basic label renderer - * class. - * - * Additionally it can be specified if a major and minor grid are rendered - * by defining a color for them. The axis label is used to add a caption - * for the axis. - * - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $start Start point of axis - * @param ezcGraphCoordinate $end Endpoint of axis - * @param ezcGraphChartElementAxis $axis Axis to render - * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer - * @return void - */ - public function drawAxis( - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphAxisLabelRenderer $labelClass = null ) - { - // Calculate used space for three dimensional effects - if ( $this->depth === false ) - { - $this->depth = min( - ( $boundings->x1 - $boundings->x0 ) * $this->options->depth, - ( $boundings->y1 - $boundings->y0 ) * $this->options->depth - ); - - $this->xDepthFactor = 1 - $this->depth / ( $boundings->x1 - $boundings->x0 ); - $this->yDepthFactor = 1 - $this->depth / ( $boundings->y1 - $boundings->y0 ); - - $this->dataBoundings = clone $boundings; - } - - // Clone boundings to not be affected by internal mofifications - $boundings = clone $boundings; - - switch ( $axis->position ) - { - case ezcGraph::TOP: - case ezcGraph::BOTTOM: - $this->xAxisSpace = ( $this->dataBoundings->x1 - $this->dataBoundings->x0 ) * $axis->axisSpace; - break; - case ezcGraph::LEFT: - case ezcGraph::RIGHT: - $this->yAxisSpace = ( $this->dataBoundings->y1 - $this->dataBoundings->y0 ) * $axis->axisSpace; - break; - } - - // Determine normalized direction - $direction = new ezcGraphVector( - $start->x - $end->x, - $start->y - $end->y - ); - $direction->unify(); - - $start->x += $boundings->x0; - $start->y += $boundings->y0; - $end->x += $boundings->x0; - $end->y += $boundings->y0; - - // Shorten drawn axis, if requested. - if ( ( $this->options->shortAxis === true ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) ) ) - { - $axisStart = clone $start; - $axisEnd = clone $end; - - $axisStart->y += $boundings->height * $axis->axisSpace * - ( $axis->position === ezcGraph::TOP ? 1 : -1 ); - $axisEnd->y -= $boundings->height * $axis->axisSpace * - ( $axis->position === ezcGraph::TOP ? 1 : -1 ); - } - elseif ( ( $this->options->shortAxis === true ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) ) ) - { - $axisStart = clone $start; - $axisEnd = clone $end; - - $axisStart->x += $boundings->width * $axis->axisSpace * - ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); - $axisEnd->x -= $boundings->width * $axis->axisSpace * - ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); - } - else - { - $axisStart = $start; - $axisEnd = $end; - } - - $axisPolygonCoordinates = array( - $this->get3dCoordinate( $axisStart, true ), - $this->get3dCoordinate( $axisEnd, true ), - $this->get3dCoordinate( $axisEnd, false ), - $this->get3dCoordinate( $axisStart, false ), - ); - - // Draw axis - if ( ( $this->options->fillAxis > 0 ) && - ( $this->options->fillAxis < 1 ) ) - { - $this->driver->drawPolygon( - $axisPolygonCoordinates, - $axis->border->transparent( $this->options->fillAxis ), - true - ); - } - else - { - $this->driver->drawPolygon( - $axisPolygonCoordinates, - $axis->border, - ! (bool) $this->options->fillAxis - ); - } - - // Draw axis lines - scedule some for later to be drawn in front of - // the data - $this->driver->drawLine( - $axisPolygonCoordinates[0], - $axisPolygonCoordinates[1], - $axis->border, - 1 - ); - - $this->frontLines[] = array( - $axisPolygonCoordinates[1], - $axisPolygonCoordinates[2], - $axis->border, - 1 - ); - - $this->frontLines[] = array( - $axisPolygonCoordinates[2], - $axisPolygonCoordinates[3], - $axis->border, - 1 - ); - - $this->frontLines[] = array( - $axisPolygonCoordinates[3], - $axisPolygonCoordinates[0], - $axis->border, - 1 - ); - - // Draw small arrowhead - $this->drawAxisArrowHead( - $axisPolygonCoordinates[1], - $direction, - max( - $axis->minArrowHeadSize, - min( - $axis->maxArrowHeadSize, - abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) - ) - ), - $axis->border - ); - - // Draw axis label - if ( $axis->label !== false ) - { - $width = $this->dataBoundings->x1 - $this->dataBoundings->x0; - switch ( $axis->position ) - { - case ezcGraph::TOP: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $axisPolygonCoordinates[2]->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ), - $axisPolygonCoordinates[2]->y - $axis->labelMargin - $axis->labelSize - ), - $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, - $axis->labelSize, - ezcGraph::TOP | ezcGraph::RIGHT - ); - break; - case ezcGraph::BOTTOM: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $axisPolygonCoordinates[1]->x + $axis->labelMargin, - $axisPolygonCoordinates[1]->y + $axis->labelMargin - ), - $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, - $axis->labelSize, - ezcGraph::TOP | ezcGraph::LEFT - ); - break; - case ezcGraph::LEFT: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $axisPolygonCoordinates[1]->x - $width, - $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin - ), - $width - $axis->labelMargin, - $axis->labelSize, - ezcGraph::BOTTOM | ezcGraph::RIGHT - ); - break; - case ezcGraph::RIGHT: - $this->driver->drawTextBox( - $axis->label, - new ezcGraphCoordinate( - $axisPolygonCoordinates[1]->x, - $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin - ), - $width - $axis->labelMargin, - $axis->labelSize, - ezcGraph::BOTTOM | ezcGraph::LEFT - ); - break; - } - } - - // Collect axis labels and draw, when all axisSpaces are collected - $this->axisLabels[] = array( - 'object' => $labelClass, - 'boundings' => $boundings, - 'start' => clone $start, - 'end' => clone $end, - 'axis' => $axis, - ); - - if ( $this->xAxisSpace && $this->yAxisSpace ) - { - foreach ( $this->axisLabels as $axisLabel ) - { - // If font should not be synchronized, use font configuration from - // each axis - if ( $this->options->syncAxisFonts === false ) - { - $this->driver->options->font = $axisLabel['axis']->font; - } - - switch ( $axisLabel['axis']->position ) - { - case ezcGraph::RIGHT: - case ezcGraph::LEFT: - $axisLabel['start']->x += $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); - $axisLabel['end']->x -= $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); - break; - case ezcGraph::TOP: - case ezcGraph::BOTTOM: - $axisLabel['start']->y += $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); - $axisLabel['end']->y -= $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); - break; - } - - $axisLabel['object']->renderLabels( - $this, - $axisLabel['boundings'], - $axisLabel['start'], - $axisLabel['end'], - $axisLabel['axis'] - ); - } - } - } - - /** - * Draw background image - * - * Draws a background image at the defined position. If repeat is set the - * background image will be repeated like any texture. - * - * @param ezcGraphBoundings $boundings Boundings for the background image - * @param string $file Filename of background image - * @param int $position Position of background image - * @param int $repeat Type of repetition - * @return void - */ - public function drawBackgroundImage( - ezcGraphBoundings $boundings, - $file, - $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE - $repeat = ezcGraph::NO_REPEAT ) - { - $imageData = getimagesize( $file ); - $imageWidth = $imageData[0]; - $imageHeight = $imageData[1]; - - $imageWidth = min( $imageWidth, $boundings->x1 - $boundings->x0 ); - $imageHeight = min( $imageHeight, $boundings->y1 - $boundings->y0 ); - - $imagePosition = new ezcGraphCoordinate( - $boundings->x0, - $boundings->y0 - ); - - // Determine x position - switch ( true ) { - case ( $repeat & ezcGraph::HORIZONTAL ): - // If is repeated on this axis fall back to position zero - case ( $position & ezcGraph::LEFT ): - $imagePosition->x = $boundings->x0; - break; - case ( $position & ezcGraph::RIGHT ): - $imagePosition->x = max( - $boundings->x1 - $imageWidth, - $boundings->x0 - ); - break; - default: - $imagePosition->x = max( - $boundings->x0 + ( $boundings->x1 - $boundings->x0 - $imageWidth ) / 2, - $boundings->x0 - ); - break; - } - - // Determine y position - switch ( true ) { - case ( $repeat & ezcGraph::VERTICAL ): - // If is repeated on this axis fall back to position zero - case ( $position & ezcGraph::TOP ): - $imagePosition->y = $boundings->y0; - break; - case ( $position & ezcGraph::BOTTOM ): - $imagePosition->y = max( - $boundings->y1 - $imageHeight, - $boundings->y0 - ); - break; - default: - $imagePosition->y = max( - $boundings->y0 + ( $boundings->y1 - $boundings->y0 - $imageHeight ) / 2, - $boundings->y0 - ); - break; - } - - // Texturize backround based on position and repetition - $position = new ezcGraphCoordinate( - $imagePosition->x, - $imagePosition->y - ); - - do - { - $position->y = $imagePosition->y; - - do - { - $this->driver->drawImage( - $file, - $position, - $imageWidth, - $imageHeight - ); - - $position->y += $imageHeight; - } - while ( ( $position->y < $boundings->y1 ) && - ( $repeat & ezcGraph::VERTICAL ) ); - - $position->x += $imageWidth; - } - while ( ( $position->x < $boundings->x1 ) && - ( $repeat & ezcGraph::HORIZONTAL ) ); - } - - /** - * Call all postprocessing functions - * - * @return void - */ - protected function finish() - { - $this->finishCirleSectors(); - $this->finishPieSegmentLabels(); - $this->finishBars(); - $this->finishLineSymbols(); - $this->finishFrontLines(); - - return true; - } - - /** - * Reset renderer properties - * - * Reset all renderer properties, which were calculated during the - * rendering process, to offer a clean environment for rerendering. - * - * @return void - */ - protected function resetRenderer() - { - parent::resetRenderer(); - - // Also reset special 3D renderer options - $this->pieSegmentLabels = array( - 0 => array(), - 1 => array(), - ); - $this->pieSegmentBoundings = false; - $this->linePostSymbols = array(); - $this->frontLines = array(); - $this->circleSectors = array(); - $this->barPostProcessing = array(); - $this->depth = false; - $this->xDepthFactor = false; - $this->yDepthFactor = false; - $this->dataBoundings = false; - $this->axisLabels = array(); - } -} - -?> + + * $graph = new ezcGraphPieChart(); + * $graph->palette = new ezcGraphPaletteEzRed(); + * $graph->title = 'Access statistics'; + * $graph->options->label = '%2$d (%3$.1f%%)'; + * + * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array( + * 'Mozilla' => 19113, + * 'Explorer' => 10917, + * 'Opera' => 1464, + * 'Safari' => 652, + * 'Konqueror' => 474, + * ) ); + * $graph->data['Access statistics']->highlight['Explorer'] = true; + * + * $graph->renderer = new ezcGraphRenderer3d(); + * + * $graph->renderer->options->moveOut = .2; + * + * $graph->renderer->options->pieChartOffset = 63; + * + * $graph->renderer->options->pieChartGleam = .3; + * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartShadowSize = 5; + * $graph->renderer->options->pieChartShadowColor = '#000000'; + * + * $graph->renderer->options->legendSymbolGleam = .5; + * $graph->renderer->options->legendSymbolGleamSize = .9; + * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; + * + * $graph->renderer->options->pieChartSymbolColor = '#55575388'; + * + * $graph->renderer->options->pieChartHeight = 5; + * $graph->renderer->options->pieChartRotation = .8; + * + * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' ); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphRenderer3d + extends + ezcGraphRenderer + implements + ezcGraphStackedBarsRenderer +{ + + /** + * Pie segment labels divided into two array, containing the labels on the + * left and right side of the pie chart center. + * + * @var array + */ + protected $pieSegmentLabels = array( + 0 => array(), + 1 => array(), + ); + + /** + * Contains the boundings used for pie segments + * + * @var ezcGraphBoundings + */ + protected $pieSegmentBoundings = false; + + /** + * Array with symbols for post processing, which ensures, that the symbols + * are rendered topmost. + * + * @var array + */ + protected $linePostSymbols = array(); + + /** + * Array containing lines from the axis and grid which should be redrawn on + * top of the data. + * + * @var array + */ + protected $frontLines = array(); + + /** + * Collects circle sectors to draw shadow in background of all circle + * sectors. + * + * @var array + */ + protected $circleSectors = array(); + + /** + * Collects bar sides to draw them in a post processing step to simulate + * a simple z buffer. + * array( + * array( + * 'index' => (int) // used for sorting + * 'context' => ezcGraphContext // context of call + * 'method' => (string) // method of driver to call + * 'parameters' => array // parameters for method call + * ), ... + * ) + * + * @var array + */ + protected $barPostProcessing = array(); + + /** + * Options + * + * @var ezcGraphRenderer3dOptions + */ + protected $options; + + /** + * Depth of displayed pseudo three dimensional line chart elements. + * + * @var float + */ + protected $depth = false; + + /** + * Factor to reduce the width according to depth + * + * @var float + */ + protected $xDepthFactor = false; + + /** + * Factor to reduce the height according to depth + * + * @var float + */ + protected $yDepthFactor = false; + + /** + * Boundings for the chart data + * + * @var ezcGraphBoundings + */ + protected $dataBoundings = false; + + /** + * Collect axis labels, so that the axis are drawn, when all axis spaces + * are known. + * + * @var array + */ + protected $axisLabels = array(); + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->options = new ezcGraphRenderer3dOptions( $options ); + } + + /** + * __get + * + * @param mixed $propertyName + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return mixed + * @ignore + */ + public function __get( $propertyName ) + { + switch ( $propertyName ) + { + case 'options': + return $this->options; + default: + return parent::__get( $propertyName ); + } + } + + /** + * Calculate the display coordinate from a coordinate + * + * Calculates the display coordinate of a coordinate depending on the + * depth setting and the distance of the coordinate to the front of the + * chart. + * + * @param ezcGraphCoordinate $c Coordinate + * @param float $front Distance to front (0 - 1) + * @return ezcGraphCoordinate Resulting coordinate + */ + protected function get3dCoordinate( ezcGraphCoordinate $c, $front = 1. ) + { + return new ezcGraphCoordinate( + ( $c->x - $this->dataBoundings->x0 ) * $this->xDepthFactor + $this->dataBoundings->x0 + $this->depth * $front, + ( $c->y - $this->dataBoundings->y0 ) * $this->yDepthFactor + $this->dataBoundings->y0 + $this->depth * ( 1 - $front ) + ); + } + + /** + * Draw pie segment + * + * Draws a single pie segment + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of pie segment + * @param float $startAngle Start angle + * @param float $endAngle End angle + * @param mixed $label Label of pie segment + * @param bool $moveOut Move out from middle for hilighting + * @return void + */ + public function drawPieSegment( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + $startAngle = .0, + $endAngle = 360., + $label = false, + $moveOut = false ) + { + // Apply offset + $startAngle += $this->options->pieChartOffset; + $endAngle += $this->options->pieChartOffset; + + // Calculate position and size of pie + $center = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + - $this->options->pieChartHeight / 2 + ); + + // Limit radius to fourth of width and half of height at maximum + $radius = min( + ( $boundings->x1 - $boundings->x0 ) * $this->options->pieHorizontalSize, + ( $boundings->y1 - $boundings->y0 ) * $this->options->pieVerticalSize + ); + + // Move pie segment out of the center + if ( $moveOut ) + { + $direction = ( $endAngle + $startAngle ) / 2; + + $center = new ezcGraphCoordinate( + $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ), + $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) * $this->options->pieChartRotation + ); + } + + // Add circle sector to queue + $this->circleSectors[] = array( + 'center' => $center, + 'context' => $context, + 'width' => $radius * 2 * ( 1 - $this->options->moveOut ), + 'height' => $radius * 2 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation - $this->options->pieChartHeight, + 'start' => $startAngle, + 'end' => $endAngle, + 'color' => $color, + ); + + if ( $label ) + { + // Determine position of label + $direction = ( $endAngle + $startAngle ) / 2; + $pieSegmentCenter = new ezcGraphCoordinate( + $center->x + cos( deg2rad( $direction ) ) * $radius, + $center->y + sin( deg2rad( $direction ) ) * $radius * $this->options->pieChartRotation + ); + + // Split labels up into left a right site and index them on their + // y position + $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][(int) ( $pieSegmentCenter->y * 100 )] = array( + new ezcGraphCoordinate( + $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3 * ( 1 - $this->options->moveOut ), + $center->y + sin( deg2rad( $direction ) ) * ( $radius - $this->options->pieChartHeight ) * 2 / 3 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation + ), + $label, + $context, + ); + } + + if ( !$this->pieSegmentBoundings ) + { + $this->pieSegmentBoundings = $boundings; + } + } + + /** + * Draws the collected pie segment labels + * + * All labels are collected and drawn later to be able to partition the + * available space for the labels woth knowledge of the overall label + * count and their required size and optimal position. + * + * @return void + */ + protected function finishPieSegmentLabels() + { + if ( $this->pieSegmentBoundings === false ) + { + return true; + } + + $boundings = $this->pieSegmentBoundings; + + // Calculate position and size of pie + $center = new ezcGraphCoordinate( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2, + $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 + ); + + // Limit radius to fourth of width and half of height at maximum + $radius = min( + ( $boundings->width ) * $this->options->pieHorizontalSize, + ( $boundings->height ) * $this->options->pieVerticalSize + ); + + $pieChartHeight = min( + $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4, + $boundings->height + ); + $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2; + + // Calculate maximum height of labels + $labelHeight = min( + ( count( $this->pieSegmentLabels[0] ) + ? $pieChartHeight / count( $this->pieSegmentLabels[0] ) + : $pieChartHeight + ), + ( count( $this->pieSegmentLabels[1] ) + ? $pieChartHeight / count( $this->pieSegmentLabels[1] ) + : $pieChartHeight + ), + ( $pieChartHeight ) * $this->options->maxLabelHeight + ); + + $symbolSize = $this->options->symbolSize; + + foreach ( $this->pieSegmentLabels as $side => $labelPart ) + { + $minHeight = $pieChartYPosition; + $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight; + + // Sort to draw topmost label first + ksort( $labelPart ); + $sign = ( $side ? -1 : 1 ); + + foreach ( $labelPart as $height => $label ) + { + $height = (int) ( $height / 100 ); + + if ( ( $height - $labelHeight / 2 ) > $minHeight ) + { + $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight ); + $minHeight += $share; + $toShare -= $share; + } + + // Determine position of label + $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare; + $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius; + + $labelPosition = new ezcGraphCoordinate( + $center->x - + $sign * ( + abs( $verticalDistance ) > 1 + // If vertical distance to center is greater then the + // radius, use the centerline for the horizontal + // position + ? max ( + 5, + abs( $label[0]->x - $center->x ) + ) + // Else place the label outside of the pie chart + : ( cos ( asin ( $verticalDistance ) ) * $radius + + $symbolSize * (int) $this->options->showSymbol + ) + ), + $minHeight + $labelHeight / 2 + ); + + if ( $this->options->showSymbol ) + { + // Draw label + $this->driver->drawLine( + $label[0], + $labelPosition, + $this->options->pieChartSymbolColor, + 1 + ); + + $this->driver->drawCircle( + $label[0], + $symbolSize, + $symbolSize, + $this->options->pieChartSymbolColor, + true + ); + $this->driver->drawCircle( + $labelPosition, + $symbolSize, + $symbolSize, + $this->options->pieChartSymbolColor, + true + ); + } + + $this->addElementReference( $label[2], + $this->driver->drawTextBox( + $label[1], + new ezcGraphCoordinate( + ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ), + $minHeight + ), + ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ), + $labelHeight, + ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE + ) + ); + + // Add used space to minHeight + $minHeight += $labelHeight; + } + } + } + + /** + * Draws the collected circle sectors + * + * All circle sectors are collected and drawn later to be able to render + * the shadows of the pie segments in the back of all pie segments, and + * ensure the correct drawing order for all pie segment elements. + * + * @return void + */ + protected function finishCirleSectors() + { + $zBuffer = array(); + + $shadows = array(); + $shadowCenter = false; + $shadowEndAngle = false; + + // Add circle sector sides to simple z buffer prioriry list + foreach ( $this->circleSectors as $circleSector ) + { + // Draw shadow if wanted + if ( $this->options->pieChartShadowSize > 0 ) + { + if ( $shadowEndAngle === false ) + { + $shadowStartAngle = $circleSector['start']; + $shadowEndAngle = $circleSector['end']; + $shadowCenter = $circleSector['center']; + } + elseif ( $circleSector['center'] == $shadowCenter ) + { + $shadowEndAngle = $circleSector['end']; + } + else + { + $shadows[] = array( + 'center' => $shadowCenter, + 'start' => $shadowStartAngle, + 'end' => $shadowEndAngle, + 'width' => $circleSector['width'], + 'height' => $circleSector['height'], + ); + + $shadowCenter = $circleSector['center']; + $shadowStartAngle = $circleSector['start']; + $shadowEndAngle = $circleSector['end']; + } + } + + $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); + + $center = (int) ( $circleSector['center']->y + sin( deg2rad( $circleSector['start'] + ( $circleSector['end'] - $circleSector['start'] ) / 2 ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight / 2 + 1 ); + + $zBuffer[$center][] = array( + 'method' => 'drawCircularArc', + 'paramenters' => array( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $this->options->pieChartHeight, + $circleSector['start'], + $circleSector['end'], + $circleSector['color'] + ) + ); + + // Left side + $polygonPoints = array( + $circleSector['center'], + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $this->options->pieChartHeight + ), + new ezcGraphCoordinate( + $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, + $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight + ), + new ezcGraphCoordinate( + $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, + $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + ), + ); + + // Get average y coordinate for polygon to use for zBuffer + $center = 0; + foreach ( $polygonPoints as $point ) + { + $center += $point->y; + } + $center = (int) ( $center / count( $polygonPoints ) ); + + $zBuffer[$center][] = array( + 'method' => 'drawPolygon', + 'paramenters' => array( + $polygonPoints, + $circleSector['color'], + true + ), + ); + + $zBuffer[$center][] = array( + 'method' => 'drawPolygon', + 'paramenters' => array( + $polygonPoints, + $darkenedColor, + false + ), + ); + + // Right side + $polygonPoints = array( + $circleSector['center'], + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $this->options->pieChartHeight + ), + new ezcGraphCoordinate( + $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, + $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight + ), + new ezcGraphCoordinate( + $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, + $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + ), + ); + + // Get average y coordinate for polygon to use for zBuffer + $center = 0; + foreach ( $polygonPoints as $point ) + { + $center += $point->y; + } + $center = (int) ( $center / count( $polygonPoints ) ); + + $zBuffer[$center][] = array( + 'method' => 'drawPolygon', + 'paramenters' => array( + $polygonPoints, + $circleSector['color'], + true + ), + ); + + $zBuffer[$center][] = array( + 'method' => 'drawPolygon', + 'paramenters' => array( + $polygonPoints, + $darkenedColor, + false + ), + ); + } + + if ( $this->options->pieChartShadowSize > 0 ) + { + $shadows[] = array( + 'center' => $shadowCenter, + 'start' => $shadowStartAngle, + 'end' => $shadowEndAngle, + 'width' => $circleSector['width'], + 'height' => $circleSector['height'], + ); + } + + // Draw collected shadows + foreach ( $shadows as $circleSector ) + { + for ( $i = $this->options->pieChartShadowSize; $i > 0; --$i ) + { + $startAngle = $circleSector['start']; + $endAngle = $circleSector['end']; + + $startAngle = $circleSector['start'] - ( $this->options->pieChartShadowSize - $i ); + $endAngle = $circleSector['end'] + ( $this->options->pieChartShadowSize - $i ); + + if ( ( $endAngle - $startAngle ) >= 360 ) + { + $this->driver->drawCircle( + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $this->options->pieChartHeight + ), + $circleSector['width'] + $i * 2, + $circleSector['height'] + $i * 2, + $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), + true + ); + } + else + { + $this->driver->drawCircleSector( + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $this->options->pieChartHeight + ), + $circleSector['width'] + $i * 2, + $circleSector['height'] + $i * 2, + $startAngle, + $endAngle, + $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), + true + ); + } + } + } + + ksort( $zBuffer ); + foreach ( $zBuffer as $sides ) + { + foreach ( $sides as $side ) + { + call_user_func_array( array( $this->driver, $side['method'] ), $side['paramenters'] ); + } + } + + // Draw circle sector for front + foreach ( $this->circleSectors as $circleSector ) + { + $this->addElementReference( $circleSector['context'], + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $circleSector['color'], + true + ) + ); + + if ( $this->options->pieChartGleam !== false ) + { + $gradient = new ezcGraphLinearGradient( + $circleSector['center'], + new ezcGraphCoordinate( + $circleSector['center']->x - $circleSector['width'] / 2, + $circleSector['center']->y - $circleSector['height'] / 2 + ), + $this->options->pieChartGleamColor->transparent( 1 ), + $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ) + ); + + $this->addElementReference( $circleSector['context'], + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'] - $this->options->pieChartGleamBorder * 2, + $circleSector['height'] - $this->options->pieChartGleamBorder * 2 * $this->options->pieChartRotation, + $circleSector['start'], + $circleSector['end'], + $gradient, + true + ) + ); + } + + $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); + $this->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $darkenedColor, + false + ); + + if ( $this->options->pieChartGleam !== false ) + { + $radialGradient = new ezcGraphRadialGradient( + new ezcGraphCoordinate( + $circleSector['center']->x + $circleSector['width'] / 2 * cos( deg2rad( 135 ) ), + $circleSector['center']->y + $circleSector['height'] / 2 * sin( deg2rad( 135 ) ) + ), + $circleSector['width'], + $circleSector['height'], + $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ), + $this->options->pieChartGleamColor->transparent( .8 ) + ); + + $this->driver->drawCircularArc( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + 0, + $circleSector['start'], + $circleSector['end'], + $radialGradient, + false + ); + } + } + } + + /** + * Draw collected front lines + * + * Draw all grid and axis lines, which should be redrawn in front of the + * data. + * + * @return void + */ + protected function finishFrontLines() + { + foreach ( $this->frontLines as $line ) + { + $this->driver->drawLine( + $line[0], + $line[1], + $line[2], + $line[3] + ); + } + } + + /** + * Draw the collected line symbols + * + * Symbols for the data lines are collected and delayed to ensure that + * they are not covered and hidden by other data lines. + * + * @return void + */ + protected function finishLineSymbols() + { + foreach ( $this->linePostSymbols as $symbol ) + { + $this->addElementReference( $symbol['context'], + $this->drawSymbol( + $symbol['boundings'], + $symbol['color'], + $symbol['symbol'] + ) + ); + } + } + + /** + * Draws a bar with a rectangular ground shape. + * + * @param ezcGraphContext $context + * @param ezcGraphColor $color + * @param ezcGraphCoordinate $position + * @param float $barWidth + * @param float $offset + * @param float $axisPosition + * @param float $startDepth + * @param float $midDepth + * @param float $endDepth + * @return void + */ + protected function drawRectangularBar( + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth ) + { + $barPolygonArray = array( + new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ), + new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, + $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ), + new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, + $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ), + new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ), + ); + + // Draw right bar side + $this->barPostProcessing[] = array( + 'index' => $barPolygonArray[2]->x + ( 1 - $position->y ), + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), + ), + $color->darken( $this->options->barDarkenSide ), + true + ), + ); + + // Draw top side + $this->barPostProcessing[] = array( + 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + ( $barPolygonArray[1]->y < $barPolygonArray[3]->y + ? array( + $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), + $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), + ) + : array( + $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), + $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), + ) + ), + $color->darken( $this->options->barDarkenTop ), + true + ), + ); + + // Draw top side gleam + if ( $this->options->barChartGleam !== false ) + { + $this->barPostProcessing[] = array( + 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + ( $barPolygonArray[1]->y < $barPolygonArray[3]->y + ? array( + $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), + $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), + ) + : array( + $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), + $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), + ) + ), + new ezcGraphLinearGradient( + ( $barPolygonArray[1]->y < $barPolygonArray[3]->y + ? $this->get3dCoordinate( $barPolygonArray[2], $endDepth ) + : $this->get3dCoordinate( $barPolygonArray[3], $endDepth ) + ), + ( $barPolygonArray[1]->y < $barPolygonArray[3]->y + ? $this->get3dCoordinate( $barPolygonArray[1], $startDepth ) + : $this->get3dCoordinate( $barPolygonArray[0], $startDepth ) + ), + ezcGraphColor::fromHex( '#FFFFFFFF' ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) + ), + true + ), + ); + } + + // Draw front side + $this->barPostProcessing[] = array( + 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + ), + $color, + true + ), + ); + + // Draw front side gleam + if ( $this->options->barChartGleam !== false ) + { + $this->barPostProcessing[] = array( + 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + ), + new ezcGraphLinearGradient( + $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), + $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), + ezcGraphColor::fromHex( '#FFFFFFFF' ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) + ), + true + ), + ); + } + } + + /** + * Draws a bar with a diamond ground shape. + * + * @param ezcGraphContext $context + * @param ezcGraphColor $color + * @param ezcGraphCoordinate $position + * @param float $barWidth + * @param float $offset + * @param float $axisPosition + * @param float $startDepth + * @param float $midDepth + * @param float $endDepth + * @return void + */ + protected function drawDiamondBar( + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth ) + { + $barCoordinateArray = array( + // The bottom point of the diamond is moved to .7 instead + // of .5 because it looks more correct, even it is wrong... + 'x' => array( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .7, + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .3, + ), + 'y' => array( + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), + ), + ); + + // Left side + $this->barPostProcessing[] = array( + 'index' => $barCoordinateArray['x'][0], + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][0] ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][1] ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), + ), + $color, + true + ), + ); + + // Right side + $this->barPostProcessing[] = array( + 'index' => $barCoordinateArray['x'][1], + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][0] ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][1] ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), + ), + $color->darken( $this->options->barDarkenSide ), + true + ), + ); + + $topLocation = min( + $barCoordinateArray['y'][0], + $barCoordinateArray['y'][1] + ); + + // Top side + $this->barPostProcessing[] = array( + 'index' => $barCoordinateArray['x'][0], + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), + ), + $color->darken( $this->options->barDarkenTop ), + true + ), + ); + + // Top side gleam + if ( $this->options->barChartGleam !== false ) + { + $this->barPostProcessing[] = array( + 'index' => $barCoordinateArray['x'][0] + 1, + 'method' => 'drawPolygon', + 'context' => $context, + 'parameters' => array( + array( + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), + ), + new ezcGraphLinearGradient( + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), + $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), + ezcGraphColor::fromHex( '#FFFFFFFF' ), + ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) + ), + true + ), + ); + } + } + + /** + * Draws a bar with a circular ground shape. + * + * @param ezcGraphContext $context + * @param ezcGraphColor $color + * @param ezcGraphCoordinate $position + * @param float $barWidth + * @param float $offset + * @param float $axisPosition + * @param float $startDepth + * @param float $midDepth + * @param float $endDepth + * @param int $symbol + * @return void + */ + protected function drawCircularBar( + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth, + $symbol ) + { + $barCenterTop = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, + $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + + ); + $barCenterBottom = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + + if ( $barCenterTop->y > $barCenterBottom->y ) + { + $tmp = $barCenterTop; + $barCenterTop = $barCenterBottom; + $barCenterBottom = $tmp; + } + + $this->barPostProcessing[] = array( + 'index' => $barCenterBottom->x, + 'method' => 'drawCircularArc', + 'context' => $context, + 'parameters' => array( + $this->get3dCoordinate( $barCenterTop, $midDepth ), + $barWidth, + $barWidth / 2, + ( $barCenterBottom->y - $barCenterTop->y ) * $this->yDepthFactor, + 0, + 180, + $color + ), + ); + + $this->barPostProcessing[] = array( + 'index' => $barCenterBottom->x + 1, + 'method' => 'drawCircle', + 'context' => $context, + 'parameters' => array( + $top = $this->get3dCoordinate( $barCenterTop, $midDepth ), + $barWidth, + $barWidth / 2, + ( $symbol === ezcGraph::CIRCLE + ? new ezcGraphLinearGradient( + new ezcGraphCoordinate( + $top->x - $barWidth / 2, + $top->y + ), + new ezcGraphCoordinate( + $top->x + $barWidth / 2, + $top->y + ), + $color->darken( $this->options->barDarkenTop ), + $color + ) + : $color + ) + ), + ); + } + + /** + * Draw bar + * + * Draws a bar as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $position Position of data point + * @param float $stepSize Space which can be used for bars + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param float $axisPosition Position of axis for drawing filled lines + * @return void + */ + public function drawBar( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $position, + $stepSize, + $dataNumber = 1, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + $axisPosition = 0. ) + { + // Apply margin + $margin = $stepSize * $this->options->barMargin; + $padding = $stepSize * $this->options->barPadding; + $barWidth = ( $stepSize - $margin ) / $dataCount - $padding; + $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2; + + if ( $barWidth < 0 ) + { + $offset -= $barWidth = abs( $barWidth ); + } + + $startDepth = $this->options->barMargin; + $midDepth = .5; + $endDepth = 1 - $this->options->barMargin; + + switch ( $symbol ) + { + case ezcGraph::NO_SYMBOL: + $this->drawRectangularBar( + $context, + $color, + $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth + ); + break; + case ezcGraph::DIAMOND: + $this->drawDiamondBar( + $context, + $color, + $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth + ); + break; + case ezcGraph::BULLET: + case ezcGraph::CIRCLE: + $this->drawCircularBar( + $context, + $color, + $position, + $barWidth, + $offset, + $axisPosition, + $startDepth, + $midDepth, + $endDepth, + $symbol + ); + break; + } + } + + /** + * Draw stacked bar + * + * Draws a stacked bar part as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $start + * @param ezcGraphCoordinate $position + * @param float $stepSize Space which can be used for bars + * @param int $symbol Symbol to draw for line + * @param float $axisPosition Position of axis for drawing filled lines + * @return void + */ + public function drawStackedBar( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $start, + ezcGraphCoordinate $position, + $stepSize, + $symbol = ezcGraph::NO_SYMBOL, + $axisPosition = 0. ) + { + // Apply margin + $margin = $stepSize * $this->options->barMargin; + $barWidth = $stepSize - $margin; + $offset = - $stepSize / 2 + $margin / 2; + + if ( $barWidth < 0 ) + { + $offset -= $barWidth = abs( $barWidth ); + } + + $startDepth = $this->options->barMargin; + $midDepth = .5; + $endDepth = 1 - $this->options->barMargin; + + switch ( $symbol ) + { + case ezcGraph::NO_SYMBOL: + case ezcGraph::DIAMOND: + case ezcGraph::BULLET: + case ezcGraph::CIRCLE: + $this->drawRectangularBar( + $context, + $color, + $position, + $barWidth, + $offset, + $start->y, + $startDepth, + $midDepth, + $endDepth + ); + break; + } + } + + /** + * Draw all collected bar elements + * + * Draw all collected bar elements after sorting them depending of their + * position to simulate simple z buffering. + * + * @access protected + * @return void + */ + protected function finishBars() + { + if ( !count( $this->barPostProcessing ) ) + { + return true; + } + + $zIndexArray = array(); + foreach ( $this->barPostProcessing as $key => $barPolygon ) + { + $zIndexArray[$key] = $barPolygon['index']; + } + + array_multisort( + $zIndexArray, SORT_ASC, SORT_NUMERIC, + $this->barPostProcessing + ); + + foreach ( $this->barPostProcessing as $bar ) + { + $this->addElementReference( $bar['context'], + call_user_func_array( + array( $this->driver, $bar['method'] ), + $bar['parameters'] + ) + ); + } + } + + /** + * Draw data line + * + * Draws a line as a data element in a line chart + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphColor $color Color of line + * @param ezcGraphCoordinate $start Starting point + * @param ezcGraphCoordinate $end Ending point + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param int $symbol Symbol to draw for line + * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor + * @param ezcGraphColor $fillColor Color to fill line with + * @param float $axisPosition Position of axis for drawing filled lines + * @param float $thickness Line thickness + * @return void + */ + public function drawDataLine( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphColor $color, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + $dataNumber = 0, + $dataCount = 1, + $symbol = ezcGraph::NO_SYMBOL, + ezcGraphColor $symbolColor = null, + ezcGraphColor $fillColor = null, + $axisPosition = 0., + $thickness = 1. ) + { + // Calculate line width based on options + if ( $this->options->seperateLines ) + { + $startDepth = ( 1 / $dataCount ) * $dataNumber; + $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); + } + else + { + $startDepth = false; + $endDepth = true; + } + + // Determine Coordinates depending on boundings and data point position + $startCoord = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $start->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + $endCoord = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + + // 3D-fy coordinates + $linePolygonPoints = array( + $this->get3dCoordinate( $startCoord, $startDepth ), + $this->get3dCoordinate( $endCoord, $startDepth ), + $this->get3dCoordinate( $endCoord, $endDepth ), + $this->get3dCoordinate( $startCoord, $endDepth ), + ); + + $startAxisCoord = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + $endAxisCoord = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + + // 3D-fy coordinates + $axisPolygonPoints = array( + $this->get3dCoordinate( $startAxisCoord, $startDepth ), + $this->get3dCoordinate( $endAxisCoord, $startDepth ), + $this->get3dCoordinate( $endAxisCoord, $endDepth ), + $this->get3dCoordinate( $startAxisCoord, $endDepth ), + ); + + // Perhaps fill up line + if ( $fillColor !== null && + $start->x != $end->x ) + { + $startValue = $axisPosition - $start->y; + $endValue = $axisPosition - $end->y; + + if ( ( $startValue == 0 ) || + ( $endValue == 0 ) || + ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) ) + { + // Values have the same sign or are on the axis + $this->driver->drawPolygon( + array( + $linePolygonPoints[0], + $linePolygonPoints[1], + $this->get3dCoordinate( $endAxisCoord, $startDepth ), + $this->get3dCoordinate( $startAxisCoord, $startDepth ), + ), + $fillColor, + true + ); + } + else + { + // values are on differente sides of the axis - split the filled polygon + $startDiff = abs( $axisPosition - $start->y ); + $endDiff = abs( $axisPosition - $end->y ); + + $cuttingPosition = $startDiff / ( $endDiff + $startDiff ); + $cuttingPoint = new ezcGraphCoordinate( + $startCoord->x + ( $endCoord->x - $startCoord->x ) * $cuttingPosition, + $startAxisCoord->y + ); + + $this->driver->drawPolygon( + array( + $this->get3dCoordinate( $startAxisCoord, $startDepth ), + $linePolygonPoints[0], + $this->get3dCoordinate( $cuttingPoint, $startDepth ), + ), + $fillColor, + true + ); + + $this->driver->drawPolygon( + array( + $this->get3dCoordinate( $endAxisCoord, $startDepth ), + $linePolygonPoints[1], + $this->get3dCoordinate( $cuttingPoint, $startDepth ), + ), + $fillColor, + true + ); + } + + // Draw closing foo + $this->driver->drawPolygon( + array( + $linePolygonPoints[2], + $linePolygonPoints[1], + $this->get3dCoordinate( $endAxisCoord, $startDepth ), + $this->get3dCoordinate( $endAxisCoord, $endDepth ), + ), + $fillColor, + true + ); + } + + + // Draw line + $this->driver->drawPolygon( + $linePolygonPoints, + $color, + true, + $thickness + ); + + // Draw polygon border + if ( $this->options->dataBorder > 0 ) + { + $this->driver->drawPolygon( + $linePolygonPoints, + $color->darken( $this->options->dataBorder ), + false, + $thickness + ); + } + + // Draw line symbol + if ( $this->options->showSymbol && + ( $symbol !== ezcGraph::NO_SYMBOL ) ) + { + if ( $symbolColor === null ) + { + $symbolColor = $color; + } + + $this->linePostSymbols[] = array( + 'boundings' => new ezcGraphBoundings( + $linePolygonPoints[2]->x - $this->options->symbolSize / 2, + $linePolygonPoints[2]->y - $this->options->symbolSize / 2, + $linePolygonPoints[2]->x + $this->options->symbolSize / 2, + $linePolygonPoints[2]->y + $this->options->symbolSize / 2 + ), + 'color' => $symbolColor, + 'context' => $context, + 'symbol' => $symbol, + ); + } + } + + /** + * Draws a highlight textbox for a datapoint. + * + * A highlight textbox for line and bar charts means a box with the current + * value in the graph. + * + * @param ezcGraphBoundings $boundings Chart boundings + * @param ezcGraphContext $context Context of call + * @param ezcGraphCoordinate $end Ending point + * @param float $axisPosition Position of axis for drawing filled lines + * @param int $dataNumber Number of dataset + * @param int $dataCount Count of datasets in chart + * @param ezcGraphFontOptions $font Font used for highlight string + * @param string $text Acutual value + * @param int $size Size of highlight text + * @param ezcGraphColor $markLines + * @param int $xOffset + * @param int $yOffset + * @param float $stepSize + * @param int $type + * @return void + */ + public function drawDataHighlightText( + ezcGraphBoundings $boundings, + ezcGraphContext $context, + ezcGraphCoordinate $end, + $axisPosition = 0., + $dataNumber = 1, + $dataCount = 1, + ezcGraphFontOptions $font, + $text, + $size, + ezcGraphColor $markLines = null, + $xOffset = 0, + $yOffset = 0, + $stepSize = 0., + $type = ezcGraph::LINE ) + { + $this->driver->options->font = $font; + $width = $this->dataBoundings->width / $dataCount; + + // Calculate line width based on options + if ( $this->options->seperateLines ) + { + $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); + } + else + { + $endDepth = true; + } + + $dataPoint = new ezcGraphCoordinate( + $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), + $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) + ); + + if ( $end->y < $axisPosition ) + { + $this->driver->drawTextBox( + $text, + $this->get3dCoordinate( new ezcGraphCoordinate( + $dataPoint->x - $width / 2, + $dataPoint->y - $size - $font->padding - $this->options->symbolSize + ), $endDepth ), + $width * $this->xDepthFactor, + $size, + ezcGraph::CENTER | ezcGraph::BOTTOM + ); + } + else + { + $this->driver->drawTextBox( + $text, + $this->get3dCoordinate( new ezcGraphCoordinate( + $dataPoint->x - $width / 2, + $dataPoint->y + $font->padding + $this->options->symbolSize + ), $endDepth ), + $width * $this->xDepthFactor, + $size, + ezcGraph::CENTER | ezcGraph::TOP + ); + } + } + + /** + * Draw legend + * + * Will draw a legend in the bounding box + * + * @param ezcGraphBoundings $boundings Bounding of legend + * @param ezcGraphChartElementLegend $legend Legend to draw; + * @param int $type Type of legend: Protrait or landscape + * @return void + */ + public function drawLegend( + ezcGraphBoundings $boundings, + ezcGraphChartElementLegend $legend, + $type = ezcGraph::VERTICAL ) + { + $labels = $legend->labels; + + // Calculate boundings of each label + if ( $type & ezcGraph::VERTICAL ) + { + $labelWidth = $boundings->x1 - $boundings->x0; + $labelHeight = min( + ( $boundings->y1 - $boundings->y0 ) / count( $labels ) - $legend->spacing, + $legend->symbolSize + 2 * $legend->padding + ); + } + else + { + $labelWidth = ( $boundings->x1 - $boundings->x0 ) / count( $labels ) - $legend->spacing; + $labelHeight = min( + $boundings->height, + $legend->symbolSize + 2 * $legend->padding + ); + } + + $symbolSize = $labelHeight - 2 * $legend->padding; + + // Draw all labels + $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ); + foreach ( $labels as $label ) + { + $this->elements['legend_url'][$label['label']] = $label['url']; + + $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol( + new ezcGraphBoundings( + $labelPosition->x + $legend->padding, + $labelPosition->y + $legend->padding, + $labelPosition->x + $legend->padding + $symbolSize, + $labelPosition->y + $legend->padding + $symbolSize + ), + $label['color'], + $label['symbol'] + ); + + $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox( + $label['label'], + new ezcGraphCoordinate( + $labelPosition->x + 2 * $legend->padding + $symbolSize, + $labelPosition->y + $legend->padding + ), + $labelWidth - $symbolSize - 3 * $legend->padding, + $labelHeight - 2 * $legend->padding, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing ); + $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 ); + } + } + + /** + * Draw box + * + * Box are wrapping each major chart element and draw border, background + * and title to each chart element. + * + * Optionally a padding and margin for each box can be defined. + * + * @param ezcGraphBoundings $boundings Boundings of the box + * @param ezcGraphColor $background Background color + * @param ezcGraphColor $borderColor Border color + * @param int $borderWidth Border width + * @param int $margin Margin + * @param int $padding Padding + * @param mixed $title Title of the box + * @param int $titleSize Size of title in the box + * @return ezcGraphBoundings Remaining inner boundings + */ + public function drawBox( + ezcGraphBoundings $boundings, + ezcGraphColor $background = null, + ezcGraphColor $borderColor = null, + $borderWidth = 0, + $margin = 0, + $padding = 0, + $title = false, + $titleSize = 16 ) + { + // Apply margin + $boundings->x0 += $margin; + $boundings->y0 += $margin; + $boundings->x1 -= $margin; + $boundings->y1 -= $margin; + + if ( $background instanceof ezcGraphColor ) + { + // Draw box background + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $background, + true + ); + } + + if ( ( $borderColor instanceof ezcGraphColor ) && + ( $borderWidth > 0 ) ) + { + // Draw border + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ), + new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ), + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ), + ), + $borderColor, + false, + $borderWidth + ); + + // Reduce local boundings by borderWidth + $boundings->x0 += $borderWidth; + $boundings->y0 += $borderWidth; + $boundings->x1 -= $borderWidth; + $boundings->y1 -= $borderWidth; + } + + // Apply padding + $boundings->x0 += $padding; + $boundings->y0 += $padding; + $boundings->x1 -= $padding; + $boundings->y1 -= $padding; + + // Add box title + if ( $title !== false ) + { + switch ( $this->options->titlePosition ) + { + case ezcGraph::TOP: + $this->driver->drawTextBox( + $title, + new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ), + $boundings->x1 - $boundings->x0, + $titleSize, + $this->options->titleAlignement + ); + + $boundings->y0 += $titleSize + $padding; + $boundings->y1 -= $titleSize + $padding; + break; + case ezcGraph::BOTTOM: + $this->driver->drawTextBox( + $title, + new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ), + $boundings->x1 - $boundings->x0, + $titleSize, + $this->options->titleAlignement + ); + + $boundings->y1 -= $titleSize + $padding; + break; + } + } + + return $boundings; + } + + /** + * Draw text + * + * Draws the provided text in the boundings + * + * @param ezcGraphBoundings $boundings Boundings of text + * @param string $text Text + * @param int $align Alignement of text + * @param ezcGraphRotation $rotation + * @return void + */ + public function drawText( + ezcGraphBoundings $boundings, + $text, + $align = ezcGraph::LEFT, + ezcGraphRotation $rotation = null ) + { + if ( $this->depth === false ) + { + // We are not 3d for now, wg. rendering normal text boxes like the + // title + $topleft = new ezcGraphCoordinate( + $boundings->x0, + $boundings->y0 + ); + $bottomright = new ezcGraphCoordinate( + $boundings->x1, + $boundings->y1 + ); + } + else + { + // The 3d part started + $topleft = $this->get3dCoordinate( + new ezcGraphCoordinate( + $boundings->x0, + $boundings->y0 + ), false + ); + $bottomright = $this->get3dCoordinate( + new ezcGraphCoordinate( + $boundings->x1, + $boundings->y1 + ), false + ); + + // Also modify rotation accordingly + if ( $rotation !== null ) + { + $rotation = new ezcGraphRotation( + $rotation->getRotation(), + $this->get3dCoordinate( $rotation->getCenter(), false ) + ); + } + } + + $this->driver->drawTextBox( + $text, + $topleft, + $bottomright->x - $topleft->x, + $bottomright->y - $topleft->y, + $align, + $rotation + ); + } + + /** + * Draw grid line + * + * Draw line for the grid in the chart background + * + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Color of the grid line + * @return void + */ + public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) + { + $gridPolygonCoordinates = array( + $this->get3dCoordinate( $start, false ), + $this->get3dCoordinate( $end, false ), + $this->get3dCoordinate( $end, true ), + $this->get3dCoordinate( $start, true ), + ); + + // Draw grid polygon + if ( $this->options->fillGrid === 0 ) + { + $this->driver->drawLine( + $gridPolygonCoordinates[2], + $gridPolygonCoordinates[3], + $color + ); + } + else + { + if ( $this->options->fillGrid === 1 ) + { + $this->driver->drawPolygon( + $gridPolygonCoordinates, + $color, + true + ); + } + else + { + $this->driver->drawPolygon( + $gridPolygonCoordinates, + $color->transparent( $this->options->fillGrid ), + true + ); + } + + // Draw grid lines - scedule some for later to be drawn in front of + // the data + $this->frontLines[] = array( + $gridPolygonCoordinates[0], + $gridPolygonCoordinates[1], + $color, + 1 + ); + + $this->frontLines[] = array( + $gridPolygonCoordinates[1], + $gridPolygonCoordinates[2], + $color, + 1 + ); + + $this->driver->drawLine( + $gridPolygonCoordinates[2], + $gridPolygonCoordinates[3], + $color, + 1 + ); + + $this->frontLines[] = array( + $gridPolygonCoordinates[3], + $gridPolygonCoordinates[0], + $color, + 1 + ); + } + } + + /** + * Draw step line + * + * Draw a step (marker for label position) on a axis. + * + * @param ezcGraphCoordinate $start Start point + * @param ezcGraphCoordinate $end End point + * @param ezcGraphColor $color Color of the grid line + * @return void + */ + public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color ) + { + $stepPolygonCoordinates = array( + $this->get3dCoordinate( $start, true ), + $this->get3dCoordinate( $end, true ), + $this->get3dCoordinate( $end, false ), + $this->get3dCoordinate( $start, false ), + ); + + // Draw step polygon + if ( ( $this->options->fillAxis > 0 ) && + ( $this->options->fillAxis < 1 ) ) + { + $this->driver->drawPolygon( + $stepPolygonCoordinates, + $color->transparent( $this->options->fillAxis ), + true + ); + + $this->driver->drawPolygon( + $stepPolygonCoordinates, + $color, + false + ); + } + else + { + $this->driver->drawPolygon( + $stepPolygonCoordinates, + $color, + ! (bool) $this->options->fillAxis + ); + } + } + + /** + * Draw axis + * + * Draws an axis form the provided start point to the end point. A specific + * angle of the axis is not required. + * + * For the labeleing of the axis a sorted array with major steps and an + * array with minor steps is expected, which are build like this: + * array( + * array( + * 'position' => (float), + * 'label' => (string), + * ) + * ) + * where the label is optional. + * + * The label renderer class defines how the labels are rendered. For more + * documentation on this topic have a look at the basic label renderer + * class. + * + * Additionally it can be specified if a major and minor grid are rendered + * by defining a color for them. The axis label is used to add a caption + * for the axis. + * + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $start Start point of axis + * @param ezcGraphCoordinate $end Endpoint of axis + * @param ezcGraphChartElementAxis $axis Axis to render + * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer + * @return void + */ + public function drawAxis( + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphAxisLabelRenderer $labelClass = null ) + { + // Calculate used space for three dimensional effects + if ( $this->depth === false ) + { + $this->depth = min( + ( $boundings->x1 - $boundings->x0 ) * $this->options->depth, + ( $boundings->y1 - $boundings->y0 ) * $this->options->depth + ); + + $this->xDepthFactor = 1 - $this->depth / ( $boundings->x1 - $boundings->x0 ); + $this->yDepthFactor = 1 - $this->depth / ( $boundings->y1 - $boundings->y0 ); + + $this->dataBoundings = clone $boundings; + } + + // Clone boundings to not be affected by internal mofifications + $boundings = clone $boundings; + + switch ( $axis->position ) + { + case ezcGraph::TOP: + case ezcGraph::BOTTOM: + $this->xAxisSpace = ( $this->dataBoundings->x1 - $this->dataBoundings->x0 ) * $axis->axisSpace; + break; + case ezcGraph::LEFT: + case ezcGraph::RIGHT: + $this->yAxisSpace = ( $this->dataBoundings->y1 - $this->dataBoundings->y0 ) * $axis->axisSpace; + break; + } + + // Determine normalized direction + $direction = new ezcGraphVector( + $start->x - $end->x, + $start->y - $end->y + ); + $direction->unify(); + + $start->x += $boundings->x0; + $start->y += $boundings->y0; + $end->x += $boundings->x0; + $end->y += $boundings->y0; + + // Shorten drawn axis, if requested. + if ( ( $this->options->shortAxis === true ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) ) ) + { + $axisStart = clone $start; + $axisEnd = clone $end; + + $axisStart->y += $boundings->height * $axis->axisSpace * + ( $axis->position === ezcGraph::TOP ? 1 : -1 ); + $axisEnd->y -= $boundings->height * $axis->axisSpace * + ( $axis->position === ezcGraph::TOP ? 1 : -1 ); + } + elseif ( ( $this->options->shortAxis === true ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) ) ) + { + $axisStart = clone $start; + $axisEnd = clone $end; + + $axisStart->x += $boundings->width * $axis->axisSpace * + ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); + $axisEnd->x -= $boundings->width * $axis->axisSpace * + ( $axis->position === ezcGraph::LEFT ? 1 : -1 ); + } + else + { + $axisStart = $start; + $axisEnd = $end; + } + + $axisPolygonCoordinates = array( + $this->get3dCoordinate( $axisStart, true ), + $this->get3dCoordinate( $axisEnd, true ), + $this->get3dCoordinate( $axisEnd, false ), + $this->get3dCoordinate( $axisStart, false ), + ); + + // Draw axis + if ( ( $this->options->fillAxis > 0 ) && + ( $this->options->fillAxis < 1 ) ) + { + $this->driver->drawPolygon( + $axisPolygonCoordinates, + $axis->border->transparent( $this->options->fillAxis ), + true + ); + } + else + { + $this->driver->drawPolygon( + $axisPolygonCoordinates, + $axis->border, + ! (bool) $this->options->fillAxis + ); + } + + // Draw axis lines - scedule some for later to be drawn in front of + // the data + $this->driver->drawLine( + $axisPolygonCoordinates[0], + $axisPolygonCoordinates[1], + $axis->border, + 1 + ); + + $this->frontLines[] = array( + $axisPolygonCoordinates[1], + $axisPolygonCoordinates[2], + $axis->border, + 1 + ); + + $this->frontLines[] = array( + $axisPolygonCoordinates[2], + $axisPolygonCoordinates[3], + $axis->border, + 1 + ); + + $this->frontLines[] = array( + $axisPolygonCoordinates[3], + $axisPolygonCoordinates[0], + $axis->border, + 1 + ); + + // Draw small arrowhead + $this->drawAxisArrowHead( + $axisPolygonCoordinates[1], + $direction, + max( + $axis->minArrowHeadSize, + min( + $axis->maxArrowHeadSize, + abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) ) + ) + ), + $axis->border + ); + + // Draw axis label + if ( $axis->label !== false ) + { + $width = $this->dataBoundings->x1 - $this->dataBoundings->x0; + switch ( $axis->position ) + { + case ezcGraph::TOP: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $axisPolygonCoordinates[2]->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ), + $axisPolygonCoordinates[2]->y - $axis->labelMargin - $axis->labelSize + ), + $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, + $axis->labelSize, + ezcGraph::TOP | ezcGraph::RIGHT + ); + break; + case ezcGraph::BOTTOM: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $axisPolygonCoordinates[1]->x + $axis->labelMargin, + $axisPolygonCoordinates[1]->y + $axis->labelMargin + ), + $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, + $axis->labelSize, + ezcGraph::TOP | ezcGraph::LEFT + ); + break; + case ezcGraph::LEFT: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $axisPolygonCoordinates[1]->x - $width, + $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin + ), + $width - $axis->labelMargin, + $axis->labelSize, + ezcGraph::BOTTOM | ezcGraph::RIGHT + ); + break; + case ezcGraph::RIGHT: + $this->driver->drawTextBox( + $axis->label, + new ezcGraphCoordinate( + $axisPolygonCoordinates[1]->x, + $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin + ), + $width - $axis->labelMargin, + $axis->labelSize, + ezcGraph::BOTTOM | ezcGraph::LEFT + ); + break; + } + } + + // Collect axis labels and draw, when all axisSpaces are collected + $this->axisLabels[] = array( + 'object' => $labelClass, + 'boundings' => $boundings, + 'start' => clone $start, + 'end' => clone $end, + 'axis' => $axis, + ); + + if ( $this->xAxisSpace && $this->yAxisSpace ) + { + foreach ( $this->axisLabels as $axisLabel ) + { + // If font should not be synchronized, use font configuration from + // each axis + if ( $this->options->syncAxisFonts === false ) + { + $this->driver->options->font = $axisLabel['axis']->font; + } + + switch ( $axisLabel['axis']->position ) + { + case ezcGraph::RIGHT: + case ezcGraph::LEFT: + $axisLabel['start']->x += $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); + $axisLabel['end']->x -= $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); + break; + case ezcGraph::TOP: + case ezcGraph::BOTTOM: + $axisLabel['start']->y += $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); + $axisLabel['end']->y -= $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); + break; + } + + $axisLabel['object']->renderLabels( + $this, + $axisLabel['boundings'], + $axisLabel['start'], + $axisLabel['end'], + $axisLabel['axis'] + ); + } + } + } + + /** + * Draw background image + * + * Draws a background image at the defined position. If repeat is set the + * background image will be repeated like any texture. + * + * @param ezcGraphBoundings $boundings Boundings for the background image + * @param string $file Filename of background image + * @param int $position Position of background image + * @param int $repeat Type of repetition + * @return void + */ + public function drawBackgroundImage( + ezcGraphBoundings $boundings, + $file, + $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE + $repeat = ezcGraph::NO_REPEAT ) + { + $imageData = getimagesize( $file ); + $imageWidth = $imageData[0]; + $imageHeight = $imageData[1]; + + $imageWidth = min( $imageWidth, $boundings->x1 - $boundings->x0 ); + $imageHeight = min( $imageHeight, $boundings->y1 - $boundings->y0 ); + + $imagePosition = new ezcGraphCoordinate( + $boundings->x0, + $boundings->y0 + ); + + // Determine x position + switch ( true ) { + case ( $repeat & ezcGraph::HORIZONTAL ): + // If is repeated on this axis fall back to position zero + case ( $position & ezcGraph::LEFT ): + $imagePosition->x = $boundings->x0; + break; + case ( $position & ezcGraph::RIGHT ): + $imagePosition->x = max( + $boundings->x1 - $imageWidth, + $boundings->x0 + ); + break; + default: + $imagePosition->x = max( + $boundings->x0 + ( $boundings->x1 - $boundings->x0 - $imageWidth ) / 2, + $boundings->x0 + ); + break; + } + + // Determine y position + switch ( true ) { + case ( $repeat & ezcGraph::VERTICAL ): + // If is repeated on this axis fall back to position zero + case ( $position & ezcGraph::TOP ): + $imagePosition->y = $boundings->y0; + break; + case ( $position & ezcGraph::BOTTOM ): + $imagePosition->y = max( + $boundings->y1 - $imageHeight, + $boundings->y0 + ); + break; + default: + $imagePosition->y = max( + $boundings->y0 + ( $boundings->y1 - $boundings->y0 - $imageHeight ) / 2, + $boundings->y0 + ); + break; + } + + // Texturize backround based on position and repetition + $position = new ezcGraphCoordinate( + $imagePosition->x, + $imagePosition->y + ); + + do + { + $position->y = $imagePosition->y; + + do + { + $this->driver->drawImage( + $file, + $position, + $imageWidth, + $imageHeight + ); + + $position->y += $imageHeight; + } + while ( ( $position->y < $boundings->y1 ) && + ( $repeat & ezcGraph::VERTICAL ) ); + + $position->x += $imageWidth; + } + while ( ( $position->x < $boundings->x1 ) && + ( $repeat & ezcGraph::HORIZONTAL ) ); + } + + /** + * Call all postprocessing functions + * + * @return void + */ + protected function finish() + { + $this->finishCirleSectors(); + $this->finishPieSegmentLabels(); + $this->finishBars(); + $this->finishLineSymbols(); + $this->finishFrontLines(); + + return true; + } + + /** + * Reset renderer properties + * + * Reset all renderer properties, which were calculated during the + * rendering process, to offer a clean environment for rerendering. + * + * @return void + */ + protected function resetRenderer() + { + parent::resetRenderer(); + + // Also reset special 3D renderer options + $this->pieSegmentLabels = array( + 0 => array(), + 1 => array(), + ); + $this->pieSegmentBoundings = false; + $this->linePostSymbols = array(); + $this->frontLines = array(); + $this->circleSectors = array(); + $this->barPostProcessing = array(); + $this->depth = false; + $this->xDepthFactor = false; + $this->yDepthFactor = false; + $this->dataBoundings = false; + $this->axisLabels = array(); + } +} + +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_boxed.php b/library/ezc/Graph/src/renderer/axis_label_boxed.php index 12ab210b2c..ac15ebe33e 100644 --- a/library/ezc/Graph/src/renderer/axis_label_boxed.php +++ b/library/ezc/Graph/src/renderer/axis_label_boxed.php @@ -1,218 +1,218 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer(); - * - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphAxisBoxedLabelRenderer extends ezcGraphAxisLabelRenderer -{ - /** - * Store step array for later coordinate modifications - * - * @var array(ezcGraphStep) - */ - protected $steps; - - /** - * Store direction for later coordinate modifications - * - * @var ezcGraphVector - */ - protected $direction; - - /** - * Store coordinate width modifier for later coordinate modifications - * - * @var float - */ - protected $widthModifier; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - parent::__construct( $options ); - $this->properties['outerStep'] = true; - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphBoundings $innerBoundings = null ) - { - // receive rendering parameters from axis - $steps = $axis->getSteps(); - $this->steps = $steps; - - $axisBoundings = new ezcGraphBoundings( - $start->x, $start->y, - $end->x, $end->y - ); - - // Determine normalized axis direction - $this->direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $this->direction->unify(); - - // Get axis space - $gridBoundings = null; - list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); - - // Determine additional required axis space by boxes - $firstStep = reset( $steps ); - $lastStep = end( $steps ); - - $this->widthModifier = 1 + $firstStep->width / 2 + $lastStep->width / 2; - - // Draw steps and grid - foreach ( $steps as $nr => $step ) - { - $position = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * ( $step->position + $step->width ) / $this->widthModifier, - $start->y + ( $end->y - $start->y ) * ( $step->position + $step->width ) / $this->widthModifier - ); - - $stepWidth = $step->width / $this->widthModifier; - - $stepSize = new ezcGraphCoordinate( - $axisBoundings->width * $stepWidth, - $axisBoundings->height * $stepWidth - ); - - if ( $this->showLabels ) - { - // Calculate label boundings - switch ( true ) - { - case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) && - ( $this->direction->x > 0 ): - $labelBoundings = new ezcGraphBoundings( - $position->x - $stepSize->x + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y + $ySpace - $this->labelPadding - ); - - $alignement = ezcGraph::CENTER | ezcGraph::TOP; - break; - case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) && - ( $this->direction->x < 0 ): - $labelBoundings = new ezcGraphBoundings( - $position->x + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x + $stepSize->x - $this->labelPadding, - $position->y + $ySpace - $this->labelPadding - ); - - $alignement = ezcGraph::CENTER | ezcGraph::TOP; - break; - case ( $this->direction->y > 0 ): - $labelBoundings = new ezcGraphBoundings( - $position->x - $xSpace + $this->labelPadding, - $position->y - $stepSize->y + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y - $this->labelPadding - ); - - $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; - break; - case ( $this->direction->y < 0 ): - $labelBoundings = new ezcGraphBoundings( - $position->x - $xSpace + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y + $stepSize->y - $this->labelPadding - ); - - $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; - break; - } - - $renderer->drawText( $labelBoundings, $step->label, $alignement ); - } - - // major grid - if ( $axis->majorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $position, - $stepSize, - $axis->majorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $position, - $this->direction, - $axis->position, - $this->majorStepSize, - $axis->border - ); - } - } - - /** - * Modify chart data position - * - * Optionally additionally modify the coodinate of a data point - * - * @param ezcGraphCoordinate $coordinate Data point coordinate - * @return ezcGraphCoordinate Modified coordinate - */ - public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) - { - $firstStep = reset( $this->steps ); - $offset = $firstStep->width / 2 / $this->widthModifier; - - return new ezcGraphCoordinate( - $coordinate->x * abs( $this->direction->y ) + - ( $coordinate->x / $this->widthModifier + $offset ) * abs( $this->direction->x ), - $coordinate->y * abs( $this->direction->x ) + - ( $coordinate->y / $this->widthModifier + $offset ) * abs( $this->direction->y ) - ); - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer(); + * + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphAxisBoxedLabelRenderer extends ezcGraphAxisLabelRenderer +{ + /** + * Store step array for later coordinate modifications + * + * @var array(ezcGraphStep) + */ + protected $steps; + + /** + * Store direction for later coordinate modifications + * + * @var ezcGraphVector + */ + protected $direction; + + /** + * Store coordinate width modifier for later coordinate modifications + * + * @var float + */ + protected $widthModifier; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + parent::__construct( $options ); + $this->properties['outerStep'] = true; + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphBoundings $innerBoundings = null ) + { + // receive rendering parameters from axis + $steps = $axis->getSteps(); + $this->steps = $steps; + + $axisBoundings = new ezcGraphBoundings( + $start->x, $start->y, + $end->x, $end->y + ); + + // Determine normalized axis direction + $this->direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $this->direction->unify(); + + // Get axis space + $gridBoundings = null; + list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); + + // Determine additional required axis space by boxes + $firstStep = reset( $steps ); + $lastStep = end( $steps ); + + $this->widthModifier = 1 + $firstStep->width / 2 + $lastStep->width / 2; + + // Draw steps and grid + foreach ( $steps as $nr => $step ) + { + $position = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * ( $step->position + $step->width ) / $this->widthModifier, + $start->y + ( $end->y - $start->y ) * ( $step->position + $step->width ) / $this->widthModifier + ); + + $stepWidth = $step->width / $this->widthModifier; + + $stepSize = new ezcGraphCoordinate( + $axisBoundings->width * $stepWidth, + $axisBoundings->height * $stepWidth + ); + + if ( $this->showLabels ) + { + // Calculate label boundings + switch ( true ) + { + case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) && + ( $this->direction->x > 0 ): + $labelBoundings = new ezcGraphBoundings( + $position->x - $stepSize->x + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y + $ySpace - $this->labelPadding + ); + + $alignement = ezcGraph::CENTER | ezcGraph::TOP; + break; + case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) && + ( $this->direction->x < 0 ): + $labelBoundings = new ezcGraphBoundings( + $position->x + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x + $stepSize->x - $this->labelPadding, + $position->y + $ySpace - $this->labelPadding + ); + + $alignement = ezcGraph::CENTER | ezcGraph::TOP; + break; + case ( $this->direction->y > 0 ): + $labelBoundings = new ezcGraphBoundings( + $position->x - $xSpace + $this->labelPadding, + $position->y - $stepSize->y + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y - $this->labelPadding + ); + + $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; + break; + case ( $this->direction->y < 0 ): + $labelBoundings = new ezcGraphBoundings( + $position->x - $xSpace + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y + $stepSize->y - $this->labelPadding + ); + + $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; + break; + } + + $renderer->drawText( $labelBoundings, $step->label, $alignement ); + } + + // major grid + if ( $axis->majorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $position, + $stepSize, + $axis->majorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $position, + $this->direction, + $axis->position, + $this->majorStepSize, + $axis->border + ); + } + } + + /** + * Modify chart data position + * + * Optionally additionally modify the coodinate of a data point + * + * @param ezcGraphCoordinate $coordinate Data point coordinate + * @return ezcGraphCoordinate Modified coordinate + */ + public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) + { + $firstStep = reset( $this->steps ); + $offset = $firstStep->width / 2 / $this->widthModifier; + + return new ezcGraphCoordinate( + $coordinate->x * abs( $this->direction->y ) + + ( $coordinate->x / $this->widthModifier + $offset ) * abs( $this->direction->x ), + $coordinate->y * abs( $this->direction->x ) + + ( $coordinate->y / $this->widthModifier + $offset ) * abs( $this->direction->y ) + ); + } +} +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_centered.php b/library/ezc/Graph/src/renderer/axis_label_centered.php index a4b866111d..2b996feed7 100644 --- a/library/ezc/Graph/src/renderer/axis_label_centered.php +++ b/library/ezc/Graph/src/renderer/axis_label_centered.php @@ -1,266 +1,266 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); - * - * - * @property bool $showZeroValue - * Show the value at the zero point of an axis. This value might be - * crossed by the other axis which would result in an unreadable - * label. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphAxisCenteredLabelRenderer extends ezcGraphAxisLabelRenderer -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['showZeroValue'] = false; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'showZeroValue': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties['showZeroValue'] = (bool) $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphBoundings $innerBoundings = null ) - { - // receive rendering parameters from axis - $steps = $axis->getSteps(); - - $axisBoundings = new ezcGraphBoundings( - $start->x, $start->y, - $end->x, $end->y - ); - - // Determine normalized axis direction - $direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $direction->unify(); - - // Get axis space - $gridBoundings = null; - list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); - - // Draw steps and grid - foreach ( $steps as $nr => $step ) - { - $position = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $step->position, - $start->y + ( $end->y - $start->y ) * $step->position - ); - $stepSize = new ezcGraphCoordinate( - $axisBoundings->width * $step->width, - $axisBoundings->height * $step->width - ); - - if ( ! $step->isZero ) - { - // major grid - if ( $axis->majorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $position, - $stepSize, - $axis->majorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $position, - $direction, - $axis->position, - $this->majorStepSize, - $axis->border - ); - } - - // draw label - if ( $this->showLabels && ( $this->showZeroValue || ! $step->isZero ) ) - { - // Calculate label boundings - if ( abs( $direction->x ) > abs( $direction->y ) ) - { - // Horizontal labels - switch ( true ) - { - case ( $nr === 0 ): - // First label - $labelSize = min( - $xSpace * 2, - $step->width * $axisBoundings->width - ); - break; - case ( $step->isLast ): - // Last label - $labelSize = min( - $xSpace * 2, - $steps[$nr - 1]->width * $axisBoundings->width - ); - break; - default: - $labelSize = min( - $step->width * $axisBoundings->width, - $steps[$nr - 1]->width * $axisBoundings->width - ); - break; - } - - $labelBoundings = new ezcGraphBoundings( - $position->x - $labelSize / 2 + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x + $labelSize / 2 - $this->labelPadding, - $position->y + $ySpace - $this->labelPadding - ); - - $alignement = ezcGraph::CENTER | ezcGraph::TOP; - } - else - { - // Vertical labels - switch ( true ) - { - case ( $nr === 0 ): - // First label - $labelSize = min( - $ySpace * 2, - $step->width * $axisBoundings->height - ); - break; - case ( $step->isLast ): - // Last label - $labelSize = min( - $ySpace * 2, - $steps[$nr - 1]->width * $axisBoundings->height - ); - break; - default: - $labelSize = min( - $step->width * $axisBoundings->height, - $steps[$nr - 1]->width * $axisBoundings->height - ); - break; - } - - $labelBoundings = new ezcGraphBoundings( - $position->x - $xSpace + $this->labelPadding, - $position->y - $labelSize / 2 + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y + $labelSize / 2 - $this->labelPadding - ); - - $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; - } - - $renderer->drawText( $labelBoundings, $step->label, $alignement ); - } - - // Iterate over minor steps - if ( !$step->isLast ) - { - foreach ( $step->childs as $minorStep ) - { - $minorStepPosition = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $minorStep->position, - $start->y + ( $end->y - $start->y ) * $minorStep->position - ); - $minorStepSize = new ezcGraphCoordinate( - $axisBoundings->width * $minorStep->width, - $axisBoundings->height * $minorStep->width - ); - - if ( $axis->minorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $minorStepPosition, - $minorStepSize, - $axis->minorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $minorStepPosition, - $direction, - $axis->position, - $this->minorStepSize, - $axis->border - ); - } - } - } - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer(); + * + * + * @property bool $showZeroValue + * Show the value at the zero point of an axis. This value might be + * crossed by the other axis which would result in an unreadable + * label. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphAxisCenteredLabelRenderer extends ezcGraphAxisLabelRenderer +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['showZeroValue'] = false; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'showZeroValue': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties['showZeroValue'] = (bool) $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphBoundings $innerBoundings = null ) + { + // receive rendering parameters from axis + $steps = $axis->getSteps(); + + $axisBoundings = new ezcGraphBoundings( + $start->x, $start->y, + $end->x, $end->y + ); + + // Determine normalized axis direction + $direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $direction->unify(); + + // Get axis space + $gridBoundings = null; + list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); + + // Draw steps and grid + foreach ( $steps as $nr => $step ) + { + $position = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $step->position, + $start->y + ( $end->y - $start->y ) * $step->position + ); + $stepSize = new ezcGraphCoordinate( + $axisBoundings->width * $step->width, + $axisBoundings->height * $step->width + ); + + if ( ! $step->isZero ) + { + // major grid + if ( $axis->majorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $position, + $stepSize, + $axis->majorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $position, + $direction, + $axis->position, + $this->majorStepSize, + $axis->border + ); + } + + // draw label + if ( $this->showLabels && ( $this->showZeroValue || ! $step->isZero ) ) + { + // Calculate label boundings + if ( abs( $direction->x ) > abs( $direction->y ) ) + { + // Horizontal labels + switch ( true ) + { + case ( $nr === 0 ): + // First label + $labelSize = min( + $xSpace * 2, + $step->width * $axisBoundings->width + ); + break; + case ( $step->isLast ): + // Last label + $labelSize = min( + $xSpace * 2, + $steps[$nr - 1]->width * $axisBoundings->width + ); + break; + default: + $labelSize = min( + $step->width * $axisBoundings->width, + $steps[$nr - 1]->width * $axisBoundings->width + ); + break; + } + + $labelBoundings = new ezcGraphBoundings( + $position->x - $labelSize / 2 + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x + $labelSize / 2 - $this->labelPadding, + $position->y + $ySpace - $this->labelPadding + ); + + $alignement = ezcGraph::CENTER | ezcGraph::TOP; + } + else + { + // Vertical labels + switch ( true ) + { + case ( $nr === 0 ): + // First label + $labelSize = min( + $ySpace * 2, + $step->width * $axisBoundings->height + ); + break; + case ( $step->isLast ): + // Last label + $labelSize = min( + $ySpace * 2, + $steps[$nr - 1]->width * $axisBoundings->height + ); + break; + default: + $labelSize = min( + $step->width * $axisBoundings->height, + $steps[$nr - 1]->width * $axisBoundings->height + ); + break; + } + + $labelBoundings = new ezcGraphBoundings( + $position->x - $xSpace + $this->labelPadding, + $position->y - $labelSize / 2 + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y + $labelSize / 2 - $this->labelPadding + ); + + $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; + } + + $renderer->drawText( $labelBoundings, $step->label, $alignement ); + } + + // Iterate over minor steps + if ( !$step->isLast ) + { + foreach ( $step->childs as $minorStep ) + { + $minorStepPosition = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $minorStep->position, + $start->y + ( $end->y - $start->y ) * $minorStep->position + ); + $minorStepSize = new ezcGraphCoordinate( + $axisBoundings->width * $minorStep->width, + $axisBoundings->height * $minorStep->width + ); + + if ( $axis->minorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $minorStepPosition, + $minorStepSize, + $axis->minorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $minorStepPosition, + $direction, + $axis->position, + $this->minorStepSize, + $axis->border + ); + } + } + } + } +} +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_exact.php b/library/ezc/Graph/src/renderer/axis_label_exact.php index 1908ab1a81..4421b351fd 100644 --- a/library/ezc/Graph/src/renderer/axis_label_exact.php +++ b/library/ezc/Graph/src/renderer/axis_label_exact.php @@ -1,304 +1,304 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer(); - * - * - * @property bool $showLastValue - * Show the last value on the axis, which will be aligned different - * than all other values, to not interfere with the arrow head of - * the axis. - * @property bool $renderLastOutside - * Render the last label outside of the normal axis label boundings - * next to the chart boundings. This may interfere with axis labels - * or cause small font size with a low axisSpace. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphAxisExactLabelRenderer extends ezcGraphAxisLabelRenderer -{ - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['showLastValue'] = true; - $this->properties['renderLastOutside'] = false; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'showLastValue': - case 'renderLastOutside': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties[$propertyName] = (bool) $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphBoundings $innerBoundings = null ) - { - // receive rendering parameters from axis - $steps = $axis->getSteps(); - - $axisBoundings = new ezcGraphBoundings( - $start->x, $start->y, - $end->x, $end->y - ); - - // Determine normalized axis direction - $direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $direction->unify(); - - // Get axis space - $gridBoundings = null; - list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); - - // Draw steps and grid - foreach ( $steps as $nr => $step ) - { - $position = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $step->position, - $start->y + ( $end->y - $start->y ) * $step->position - ); - $stepSize = new ezcGraphCoordinate( - $axisBoundings->width * $step->width, - $axisBoundings->height * $step->width - ); - - if ( ! $step->isZero ) - { - // major grid - if ( $axis->majorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $position, - $stepSize, - $axis->majorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $position, - $direction, - $axis->position, - $this->majorStepSize, - $axis->border - ); - } - - if ( $this->showLabels ) - { - switch ( $axis->position ) - { - case ezcGraph::RIGHT: - case ezcGraph::LEFT: - $labelWidth = $axisBoundings->width * - $steps[$nr - $step->isLast]->width / - ( $this->showLastValue + 1 ); - $labelHeight = $ySpace; - - if ( ( $this->renderLastOutside === true ) && - ( $step->isLast === true ) ) - { - $labelWidth = ( $boundings->width - $axisBoundings->width ) / 2; - } - break; - - case ezcGraph::BOTTOM: - case ezcGraph::TOP: - $labelWidth = $xSpace; - $labelHeight = $axisBoundings->height * - $steps[$nr - $step->isLast]->width / - ( $this->showLastValue + 1 ); - - if ( ( $this->renderLastOutside === true ) && - ( $step->isLast === true ) ) - { - $labelHeight = ( $boundings->height - $axisBoundings->height ) / 2; - } - break; - } - - $showLabel = true; - switch ( true ) - { - case ( !$this->showLastValue && $step->isLast ): - // Skip last step if showLastValue is false - $showLabel = false; - break; - // Draw label at top left of step - case ( ( $axis->position === ezcGraph::BOTTOM ) && - ( !$step->isLast ) ) || - ( ( $axis->position === ezcGraph::BOTTOM ) && - ( $step->isLast ) && - ( $this->renderLastOutside ) ) || - ( ( $axis->position === ezcGraph::TOP ) && - ( $step->isLast ) && - ( !$this->renderLastOutside ) ): - $labelBoundings = new ezcGraphBoundings( - $position->x - $labelWidth + $this->labelPadding, - $position->y - $labelHeight + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y - $this->labelPadding - ); - $alignement = ezcGraph::RIGHT | ezcGraph::BOTTOM; - break; - // Draw label at bottom right of step - case ( ( $axis->position === ezcGraph::LEFT ) && - ( !$step->isLast ) ) || - ( ( $axis->position === ezcGraph::LEFT ) && - ( $step->isLast ) && - ( $this->renderLastOutside ) ) || - ( ( $axis->position === ezcGraph::RIGHT ) && - ( $step->isLast ) && - ( !$this->renderLastOutside ) ): - $labelBoundings = new ezcGraphBoundings( - $position->x + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x + $labelWidth - $this->labelPadding, - $position->y + $labelHeight - $this->labelPadding - ); - $alignement = ezcGraph::LEFT | ezcGraph::TOP; - break; - // Draw label at bottom left of step - case ( ( $axis->position === ezcGraph::TOP ) && - ( !$step->isLast ) ) || - ( ( $axis->position === ezcGraph::TOP ) && - ( $step->isLast ) && - ( $this->renderLastOutside ) ) || - ( ( $axis->position === ezcGraph::RIGHT ) && - ( !$step->isLast ) ) || - ( ( $axis->position === ezcGraph::RIGHT ) && - ( $step->isLast ) && - ( $this->renderLastOutside ) ) || - ( ( $axis->position === ezcGraph::BOTTOM ) && - ( $step->isLast ) && - ( !$this->renderLastOutside ) ) || - ( ( $axis->position === ezcGraph::LEFT ) && - ( $step->isLast ) && - ( !$this->renderLastOutside ) ): - $labelBoundings = new ezcGraphBoundings( - $position->x - $labelWidth + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y + $labelHeight - $this->labelPadding - ); - $alignement = ezcGraph::RIGHT | ezcGraph::TOP; - break; - } - - if ( $showLabel ) - { - $renderer->drawText( - $labelBoundings, - $step->label, - $alignement - ); - } - } - - if ( !$step->isLast ) - { - // Iterate over minor steps - foreach ( $step->childs as $minorStep ) - { - $minorStepPosition = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $minorStep->position, - $start->y + ( $end->y - $start->y ) * $minorStep->position - ); - $minorStepSize = new ezcGraphCoordinate( - $axisBoundings->width * $minorStep->width, - $axisBoundings->height * $minorStep->width - ); - - if ( $axis->minorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $minorStepPosition, - $minorStepSize, - $axis->minorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $minorStepPosition, - $direction, - $axis->position, - $this->minorStepSize, - $axis->border - ); - } - } - } - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer(); + * + * + * @property bool $showLastValue + * Show the last value on the axis, which will be aligned different + * than all other values, to not interfere with the arrow head of + * the axis. + * @property bool $renderLastOutside + * Render the last label outside of the normal axis label boundings + * next to the chart boundings. This may interfere with axis labels + * or cause small font size with a low axisSpace. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphAxisExactLabelRenderer extends ezcGraphAxisLabelRenderer +{ + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['showLastValue'] = true; + $this->properties['renderLastOutside'] = false; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'showLastValue': + case 'renderLastOutside': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties[$propertyName] = (bool) $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphBoundings $innerBoundings = null ) + { + // receive rendering parameters from axis + $steps = $axis->getSteps(); + + $axisBoundings = new ezcGraphBoundings( + $start->x, $start->y, + $end->x, $end->y + ); + + // Determine normalized axis direction + $direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $direction->unify(); + + // Get axis space + $gridBoundings = null; + list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); + + // Draw steps and grid + foreach ( $steps as $nr => $step ) + { + $position = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $step->position, + $start->y + ( $end->y - $start->y ) * $step->position + ); + $stepSize = new ezcGraphCoordinate( + $axisBoundings->width * $step->width, + $axisBoundings->height * $step->width + ); + + if ( ! $step->isZero ) + { + // major grid + if ( $axis->majorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $position, + $stepSize, + $axis->majorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $position, + $direction, + $axis->position, + $this->majorStepSize, + $axis->border + ); + } + + if ( $this->showLabels ) + { + switch ( $axis->position ) + { + case ezcGraph::RIGHT: + case ezcGraph::LEFT: + $labelWidth = $axisBoundings->width * + $steps[$nr - $step->isLast]->width / + ( $this->showLastValue + 1 ); + $labelHeight = $ySpace; + + if ( ( $this->renderLastOutside === true ) && + ( $step->isLast === true ) ) + { + $labelWidth = ( $boundings->width - $axisBoundings->width ) / 2; + } + break; + + case ezcGraph::BOTTOM: + case ezcGraph::TOP: + $labelWidth = $xSpace; + $labelHeight = $axisBoundings->height * + $steps[$nr - $step->isLast]->width / + ( $this->showLastValue + 1 ); + + if ( ( $this->renderLastOutside === true ) && + ( $step->isLast === true ) ) + { + $labelHeight = ( $boundings->height - $axisBoundings->height ) / 2; + } + break; + } + + $showLabel = true; + switch ( true ) + { + case ( !$this->showLastValue && $step->isLast ): + // Skip last step if showLastValue is false + $showLabel = false; + break; + // Draw label at top left of step + case ( ( $axis->position === ezcGraph::BOTTOM ) && + ( !$step->isLast ) ) || + ( ( $axis->position === ezcGraph::BOTTOM ) && + ( $step->isLast ) && + ( $this->renderLastOutside ) ) || + ( ( $axis->position === ezcGraph::TOP ) && + ( $step->isLast ) && + ( !$this->renderLastOutside ) ): + $labelBoundings = new ezcGraphBoundings( + $position->x - $labelWidth + $this->labelPadding, + $position->y - $labelHeight + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y - $this->labelPadding + ); + $alignement = ezcGraph::RIGHT | ezcGraph::BOTTOM; + break; + // Draw label at bottom right of step + case ( ( $axis->position === ezcGraph::LEFT ) && + ( !$step->isLast ) ) || + ( ( $axis->position === ezcGraph::LEFT ) && + ( $step->isLast ) && + ( $this->renderLastOutside ) ) || + ( ( $axis->position === ezcGraph::RIGHT ) && + ( $step->isLast ) && + ( !$this->renderLastOutside ) ): + $labelBoundings = new ezcGraphBoundings( + $position->x + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x + $labelWidth - $this->labelPadding, + $position->y + $labelHeight - $this->labelPadding + ); + $alignement = ezcGraph::LEFT | ezcGraph::TOP; + break; + // Draw label at bottom left of step + case ( ( $axis->position === ezcGraph::TOP ) && + ( !$step->isLast ) ) || + ( ( $axis->position === ezcGraph::TOP ) && + ( $step->isLast ) && + ( $this->renderLastOutside ) ) || + ( ( $axis->position === ezcGraph::RIGHT ) && + ( !$step->isLast ) ) || + ( ( $axis->position === ezcGraph::RIGHT ) && + ( $step->isLast ) && + ( $this->renderLastOutside ) ) || + ( ( $axis->position === ezcGraph::BOTTOM ) && + ( $step->isLast ) && + ( !$this->renderLastOutside ) ) || + ( ( $axis->position === ezcGraph::LEFT ) && + ( $step->isLast ) && + ( !$this->renderLastOutside ) ): + $labelBoundings = new ezcGraphBoundings( + $position->x - $labelWidth + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y + $labelHeight - $this->labelPadding + ); + $alignement = ezcGraph::RIGHT | ezcGraph::TOP; + break; + } + + if ( $showLabel ) + { + $renderer->drawText( + $labelBoundings, + $step->label, + $alignement + ); + } + } + + if ( !$step->isLast ) + { + // Iterate over minor steps + foreach ( $step->childs as $minorStep ) + { + $minorStepPosition = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $minorStep->position, + $start->y + ( $end->y - $start->y ) * $minorStep->position + ); + $minorStepSize = new ezcGraphCoordinate( + $axisBoundings->width * $minorStep->width, + $axisBoundings->height * $minorStep->width + ); + + if ( $axis->minorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $minorStepPosition, + $minorStepSize, + $axis->minorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $minorStepPosition, + $direction, + $axis->position, + $this->minorStepSize, + $axis->border + ); + } + } + } + } +} +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_none.php b/library/ezc/Graph/src/renderer/axis_label_none.php index 9d2b29c940..120a55432c 100644 --- a/library/ezc/Graph/src/renderer/axis_label_none.php +++ b/library/ezc/Graph/src/renderer/axis_label_none.php @@ -1,44 +1,44 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer(); - * - * - * @version //autogentag// - * @package Graph - */ -class ezcGraphAxisNoLabelRenderer extends ezcGraphAxisLabelRenderer -{ - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis ) - { - return true; - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer(); + * + * + * @version 1.4.3 + * @package Graph + */ +class ezcGraphAxisNoLabelRenderer extends ezcGraphAxisLabelRenderer +{ + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis ) + { + return true; + } +} +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_radar.php b/library/ezc/Graph/src/renderer/axis_label_radar.php index 476c1a826d..f7d28b94f9 100644 --- a/library/ezc/Graph/src/renderer/axis_label_radar.php +++ b/library/ezc/Graph/src/renderer/axis_label_radar.php @@ -1,322 +1,322 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); - * - * - * @property float $lastStep - * Position of last step on the axis to calculate the grid. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphAxisRadarLabelRenderer extends ezcGraphAxisLabelRenderer -{ - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - $this->properties['lastStep'] = null; - - parent::__construct( $options ); - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'lastStep': - if ( !is_null( $propertyValue ) && - ( !is_float( $propertyValue ) || - ( $propertyValue < 0 ) || - ( $propertyValue > 1 ) ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); - } - - $this->properties['lastStep'] = $propertyValue; - break; - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis ) - { - // receive rendering parameters from axis - $steps = $axis->getSteps(); - - $axisBoundings = new ezcGraphBoundings( - $start->x, $start->y, - $end->x, $end->y - ); - - // Determine normalized axis direction - $direction = new ezcGraphVector( - $start->x - $end->x, - $start->y - $end->y - ); - $direction->unify(); - - // Draw steps and grid - foreach ( $steps as $nr => $step ) - { - $position = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $step->position, - $start->y + ( $end->y - $start->y ) * $step->position - ); - $stepSize = new ezcGraphCoordinate( - $axisBoundings->width * $step->width, - $axisBoundings->height * $step->width - ); - - // Draw major grid - if ( ( $this->lastStep !== null ) && $axis->majorGrid ) - { - $this->drawGrid( - $renderer, - $boundings, - $position, - $stepSize, - $axis->majorGrid, - $step->position - ); - } - - // major step - $this->drawStep( - $renderer, - $position, - $direction, - $axis->position, - $this->majorStepSize, - $axis->border - ); - - // draw label - if ( $this->showLabels && ( $this->lastStep === null ) ) - { - // Calculate label boundings - if ( abs( $direction->x ) > abs( $direction->y ) ) - { - // Horizontal labels - switch ( true ) - { - case ( $nr === 0 ): - // First label - $labelSize = min( - $renderer->xAxisSpace * 2, - $step->width * $axisBoundings->width - ); - break; - case ( $step->isLast ): - // Last label - $labelSize = min( - $renderer->xAxisSpace * 2, - $steps[$nr - 1]->width * $axisBoundings->width - ); - break; - default: - $labelSize = min( - $step->width * $axisBoundings->width, - $steps[$nr - 1]->width * $axisBoundings->width - ); - break; - } - - $labelBoundings = new ezcGraphBoundings( - $position->x - $labelSize / 2 + $this->labelPadding, - $position->y + $this->labelPadding, - $position->x + $labelSize / 2 - $this->labelPadding, - $position->y + $renderer->yAxisSpace - $this->labelPadding - ); - - $alignement = ezcGraph::CENTER | ezcGraph::TOP; - } - else - { - // Vertical labels - switch ( true ) - { - case ( $nr === 0 ): - // First label - $labelSize = min( - $renderer->yAxisSpace * 2, - $step->width * $axisBoundings->height - ); - break; - case ( $step->isLast ): - // Last label - $labelSize = min( - $renderer->yAxisSpace * 2, - $steps[$nr - 1]->width * $axisBoundings->height - ); - break; - default: - $labelSize = min( - $step->width * $axisBoundings->height, - $steps[$nr - 1]->width * $axisBoundings->height - ); - break; - } - - $labelBoundings = new ezcGraphBoundings( - $position->x - $renderer->xAxisSpace + $this->labelPadding, - $position->y - $labelSize / 2 + $this->labelPadding, - $position->x - $this->labelPadding, - $position->y + $labelSize / 2 - $this->labelPadding - ); - - $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; - } - - $renderer->drawText( $labelBoundings, $step->label, $alignement ); - } - - // Iterate over minor steps - if ( !$step->isLast ) - { - foreach ( $step->childs as $minorStep ) - { - $minorStepPosition = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $minorStep->position, - $start->y + ( $end->y - $start->y ) * $minorStep->position - ); - $minorStepSize = new ezcGraphCoordinate( - $axisBoundings->width * $minorStep->width, - $axisBoundings->height * $minorStep->width - ); - - if ( ( $this->lastStep !== null ) && $axis->minorGrid ) - { - $this->drawGrid( - $renderer, - $boundings, - $minorStepPosition, - $minorStepSize, - $axis->minorGrid, - $minorStep->position - ); - } - - // major step - $this->drawStep( - $renderer, - $minorStepPosition, - $direction, - $axis->position, - $this->minorStepSize, - $axis->border - ); - } - } - } - } - - /** - * Draw grid - * - * Draws a grid line at the current position - * - * @param ezcGraphRenderer $renderer Renderer to draw the grid with - * @param ezcGraphBoundings $boundings Boundings of axis - * @param ezcGraphCoordinate $position Position of step - * @param ezcGraphCoordinate $direction Direction of axis - * @param ezcGraphColor $color Color of axis - * @param int $stepPosition - * @return void - */ - protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color, $stepPosition = null ) - { - // Calculate position on last axis - $start = new ezcGraphCoordinate( - $boundings->x0 + $width = ( $boundings->width / 2 ), - $boundings->y0 + $height = ( $boundings->height / 2 ) - ); - - $lastAngle = $this->lastStep * 2 * M_PI; - $end = new ezcGraphCoordinate( - $start->x + sin( $lastAngle ) * $width, - $start->y - cos( $lastAngle ) * $height - ); - - $direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $direction->unify(); - - // Convert elipse to circle for correct angle calculation - $direction->y *= ( $renderer->xAxisSpace / $renderer->yAxisSpace ); - $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); - - $movement = new ezcGraphVector( - sin( $angle ) * $renderer->xAxisSpace - * ( $direction->x < 0 ? -1 : 1 ), - cos( $angle ) * $renderer->yAxisSpace - ); - - $start->x += $movement->x; - $start->y += $movement->y; - $end->x -= $movement->x; - $end->y -= $movement->y; - - $lastPosition = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $stepPosition, - $start->y + ( $end->y - $start->y ) * $stepPosition - ); - - $renderer->drawGridLine( - $position, - $lastPosition, - $color - ); - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer(); + * + * + * @property float $lastStep + * Position of last step on the axis to calculate the grid. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphAxisRadarLabelRenderer extends ezcGraphAxisLabelRenderer +{ + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + $this->properties['lastStep'] = null; + + parent::__construct( $options ); + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'lastStep': + if ( !is_null( $propertyValue ) && + ( !is_float( $propertyValue ) || + ( $propertyValue < 0 ) || + ( $propertyValue > 1 ) ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' ); + } + + $this->properties['lastStep'] = $propertyValue; + break; + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis ) + { + // receive rendering parameters from axis + $steps = $axis->getSteps(); + + $axisBoundings = new ezcGraphBoundings( + $start->x, $start->y, + $end->x, $end->y + ); + + // Determine normalized axis direction + $direction = new ezcGraphVector( + $start->x - $end->x, + $start->y - $end->y + ); + $direction->unify(); + + // Draw steps and grid + foreach ( $steps as $nr => $step ) + { + $position = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $step->position, + $start->y + ( $end->y - $start->y ) * $step->position + ); + $stepSize = new ezcGraphCoordinate( + $axisBoundings->width * $step->width, + $axisBoundings->height * $step->width + ); + + // Draw major grid + if ( ( $this->lastStep !== null ) && $axis->majorGrid ) + { + $this->drawGrid( + $renderer, + $boundings, + $position, + $stepSize, + $axis->majorGrid, + $step->position + ); + } + + // major step + $this->drawStep( + $renderer, + $position, + $direction, + $axis->position, + $this->majorStepSize, + $axis->border + ); + + // draw label + if ( $this->showLabels && ( $this->lastStep === null ) ) + { + // Calculate label boundings + if ( abs( $direction->x ) > abs( $direction->y ) ) + { + // Horizontal labels + switch ( true ) + { + case ( $nr === 0 ): + // First label + $labelSize = min( + $renderer->xAxisSpace * 2, + $step->width * $axisBoundings->width + ); + break; + case ( $step->isLast ): + // Last label + $labelSize = min( + $renderer->xAxisSpace * 2, + $steps[$nr - 1]->width * $axisBoundings->width + ); + break; + default: + $labelSize = min( + $step->width * $axisBoundings->width, + $steps[$nr - 1]->width * $axisBoundings->width + ); + break; + } + + $labelBoundings = new ezcGraphBoundings( + $position->x - $labelSize / 2 + $this->labelPadding, + $position->y + $this->labelPadding, + $position->x + $labelSize / 2 - $this->labelPadding, + $position->y + $renderer->yAxisSpace - $this->labelPadding + ); + + $alignement = ezcGraph::CENTER | ezcGraph::TOP; + } + else + { + // Vertical labels + switch ( true ) + { + case ( $nr === 0 ): + // First label + $labelSize = min( + $renderer->yAxisSpace * 2, + $step->width * $axisBoundings->height + ); + break; + case ( $step->isLast ): + // Last label + $labelSize = min( + $renderer->yAxisSpace * 2, + $steps[$nr - 1]->width * $axisBoundings->height + ); + break; + default: + $labelSize = min( + $step->width * $axisBoundings->height, + $steps[$nr - 1]->width * $axisBoundings->height + ); + break; + } + + $labelBoundings = new ezcGraphBoundings( + $position->x - $renderer->xAxisSpace + $this->labelPadding, + $position->y - $labelSize / 2 + $this->labelPadding, + $position->x - $this->labelPadding, + $position->y + $labelSize / 2 - $this->labelPadding + ); + + $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT; + } + + $renderer->drawText( $labelBoundings, $step->label, $alignement ); + } + + // Iterate over minor steps + if ( !$step->isLast ) + { + foreach ( $step->childs as $minorStep ) + { + $minorStepPosition = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $minorStep->position, + $start->y + ( $end->y - $start->y ) * $minorStep->position + ); + $minorStepSize = new ezcGraphCoordinate( + $axisBoundings->width * $minorStep->width, + $axisBoundings->height * $minorStep->width + ); + + if ( ( $this->lastStep !== null ) && $axis->minorGrid ) + { + $this->drawGrid( + $renderer, + $boundings, + $minorStepPosition, + $minorStepSize, + $axis->minorGrid, + $minorStep->position + ); + } + + // major step + $this->drawStep( + $renderer, + $minorStepPosition, + $direction, + $axis->position, + $this->minorStepSize, + $axis->border + ); + } + } + } + } + + /** + * Draw grid + * + * Draws a grid line at the current position + * + * @param ezcGraphRenderer $renderer Renderer to draw the grid with + * @param ezcGraphBoundings $boundings Boundings of axis + * @param ezcGraphCoordinate $position Position of step + * @param ezcGraphCoordinate $direction Direction of axis + * @param ezcGraphColor $color Color of axis + * @param int $stepPosition + * @return void + */ + protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color, $stepPosition = null ) + { + // Calculate position on last axis + $start = new ezcGraphCoordinate( + $boundings->x0 + $width = ( $boundings->width / 2 ), + $boundings->y0 + $height = ( $boundings->height / 2 ) + ); + + $lastAngle = $this->lastStep * 2 * M_PI; + $end = new ezcGraphCoordinate( + $start->x + sin( $lastAngle ) * $width, + $start->y - cos( $lastAngle ) * $height + ); + + $direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $direction->unify(); + + // Convert elipse to circle for correct angle calculation + $direction->y *= ( $renderer->xAxisSpace / $renderer->yAxisSpace ); + $angle = $direction->angle( new ezcGraphVector( 0, 1 ) ); + + $movement = new ezcGraphVector( + sin( $angle ) * $renderer->xAxisSpace + * ( $direction->x < 0 ? -1 : 1 ), + cos( $angle ) * $renderer->yAxisSpace + ); + + $start->x += $movement->x; + $start->y += $movement->y; + $end->x -= $movement->x; + $end->y -= $movement->y; + + $lastPosition = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $stepPosition, + $start->y + ( $end->y - $start->y ) * $stepPosition + ); + + $renderer->drawGridLine( + $position, + $lastPosition, + $color + ); + } +} +?> diff --git a/library/ezc/Graph/src/renderer/axis_label_rotated.php b/library/ezc/Graph/src/renderer/axis_label_rotated.php index 9540be8317..a8f5d9aaf5 100644 --- a/library/ezc/Graph/src/renderer/axis_label_rotated.php +++ b/library/ezc/Graph/src/renderer/axis_label_rotated.php @@ -1,433 +1,433 @@ - - * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRotatedLabelRenderer(); - * - * // Define angle manually in degree - * $chart->xAxis->axisLabelRenderer->angle = 45; - * - * // Increase axis space - * $chart->xAxis->axisSpace = .2; - * - * - * @property float $angle - * Angle of labels on axis in degrees. - * - * @version //autogentag// - * @package Graph - * @mainclass - */ -class ezcGraphAxisRotatedLabelRenderer extends ezcGraphAxisLabelRenderer -{ - /** - * Store step array for later coordinate modifications - * - * @var array(ezcGraphStep) - */ - protected $steps; - - /** - * Store direction for later coordinate modifications - * - * @var ezcGraphVector - */ - protected $direction; - - /** - * Store coordinate width modifier for later coordinate modifications - * - * @var float - */ - protected $widthModifier; - - /** - * Store coordinate offset for later coordinate modifications - * - * @var float - */ - protected $offset; - - /** - * Constructor - * - * @param array $options Default option array - * @return void - * @ignore - */ - public function __construct( array $options = array() ) - { - parent::__construct( $options ); - $this->properties['angle'] = null; - $this->properties['labelOffset'] = true; - } - - /** - * __set - * - * @param mixed $propertyName - * @param mixed $propertyValue - * @throws ezcBaseValueException - * If a submitted parameter was out of range or type. - * @throws ezcBasePropertyNotFoundException - * If a the value for the property options is not an instance of - * @return void - * @ignore - */ - public function __set( $propertyName, $propertyValue ) - { - switch ( $propertyName ) - { - case 'angle': - if ( !is_numeric( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 360' ); - } - - $reducement = (int) ( $propertyValue - $propertyValue % 360 ); - $this->properties['angle'] = (float) $propertyValue - $reducement; - break; - - case 'labelOffset': - if ( !is_bool( $propertyValue ) ) - { - throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); - } - - $this->properties[$propertyName] = (bool) $propertyValue; - break; - - default: - return parent::__set( $propertyName, $propertyValue ); - } - } - - /** - * Render Axis labels - * - * Render labels for an axis. - * - * @param ezcGraphRenderer $renderer Renderer used to draw the chart - * @param ezcGraphBoundings $boundings Boundings of the axis - * @param ezcGraphCoordinate $start Axis starting point - * @param ezcGraphCoordinate $end Axis ending point - * @param ezcGraphChartElementAxis $axis Axis instance - * @return void - */ - public function renderLabels( - ezcGraphRenderer $renderer, - ezcGraphBoundings $boundings, - ezcGraphCoordinate $start, - ezcGraphCoordinate $end, - ezcGraphChartElementAxis $axis, - ezcGraphBoundings $innerBoundings = null ) - { - // receive rendering parameters from axis - $steps = $axis->getSteps(); - $this->steps = $steps; - - $axisBoundings = new ezcGraphBoundings( - $start->x, $start->y, - $end->x, $end->y - ); - - // Determine normalized axis direction - $this->direction = new ezcGraphVector( - $end->x - $start->x, - $end->y - $start->y - ); - $this->direction->unify(); - $axisAngle = -$this->direction->angle( new ezcGraphVector( 1, 0 ) ); - - // Get axis space - $gridBoundings = null; - list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); - - // Determine optimal angle if none specified - if ( $this->angle === null ) - { - $minimumStepWidth = null; - foreach ( $steps as $nr => $step ) - { - if ( ( $minimumStepWidth === null ) || - ( $step->width < $minimumStepWidth ) ) - { - $minimumStepWidth = $step->width; - } - } - - $width = abs( - $axisBoundings->width * $minimumStepWidth * $this->direction->x + - $axisBoundings->height * $minimumStepWidth * $this->direction->y - ); - $height = abs( - $ySpace * $this->direction->x + - $xSpace * $this->direction->y - ); - - $length = sqrt( pow( $width, 2 ) + pow( $height, 2 ) ); - $this->angle = rad2deg( acos( $height / $length ) ); - } - - // Determine additional required axis space by boxes - $firstStep = reset( $steps ); - $lastStep = end( $steps ); - - $textAngle = $axisAngle + - deg2rad( $this->angle ) + - ( $axis->position & ( ezcGraph::TOP | ezcGraph::BOTTOM ) ? deg2rad( 270 ) : deg2rad( 90 ) ); - - // Ensure angle between 0 and 360 degrees - $degTextAngle = rad2deg( $textAngle ); - while ( $degTextAngle < 0 ) - { - $degTextAngle += 360.; - } - - if ( $this->properties['labelOffset'] ) - { - $this->offset = - ( $this->angle < 0 ? -1 : 1 ) * - ( $axis->position & ( ezcGraph::TOP | ezcGraph::LEFT ) ? 1 : -1 ) * - ( 1 - cos( deg2rad( $this->angle * 2 ) ) ); - } - else - { - $this->offset = 0; - } - - $axisSpaceFactor = abs( - ( $this->direction->x == 0 ? 0 : - $this->direction->x * $ySpace / $axisBoundings->width ) + - ( $this->direction->y == 0 ? 0 : - $this->direction->y * $xSpace / $axisBoundings->height ) - ); - - $start = new ezcGraphCoordinate( - $start->x + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ), - $start->y + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y ) - ); - $end = new ezcGraphCoordinate( - $end->x + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ), - $end->y + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y ) - ); - - $labelLength = sqrt( - pow( - $xSpace * $this->direction->y + - $axisSpaceFactor * $this->offset * ( $end->x - $start->x ), - 2 ) + - pow( - $ySpace * $this->direction->x + - $axisSpaceFactor * $this->offset * ( $end->y - $start->y ), - 2 ) - ); - - $this->offset *= $axisSpaceFactor; - - // Draw steps and grid - foreach ( $steps as $nr => $step ) - { - $position = new ezcGraphCoordinate( - $start->x + ( $end->x - $start->x ) * $step->position * abs( $this->direction->x ), - $start->y + ( $end->y - $start->y ) * $step->position * abs( $this->direction->y ) - ); - - $stepSize = new ezcGraphCoordinate( - ( $end->x - $start->x ) * $step->width, - ( $end->y - $start->y ) * $step->width - ); - - // Calculate label boundings - switch ( true ) - { - case ( $nr === 0 ): - $labelSize = min( - abs( - $xSpace * 2 * $this->direction->y + - $ySpace * 2 * $this->direction->x ), - abs( - $step->width * $axisBoundings->width * $this->direction->x + - $step->width * $axisBoundings->height * $this->direction->y ) - ); - break; - case ( $step->isLast ): - $labelSize = min( - abs( - $xSpace * 2 * $this->direction->y + - $ySpace * 2 * $this->direction->x ), - abs( - $steps[$nr - 1]->width * $axisBoundings->width * $this->direction->x + - $steps[$nr - 1]->width * $axisBoundings->height * $this->direction->y ) - ); - break; - default: - $labelSize = abs( - $step->width * $axisBoundings->width * $this->direction->x + - $step->width * $axisBoundings->height * $this->direction->y - ); - break; - } - - $labelSize = $labelSize * cos( deg2rad( $this->angle ) ); - $lengthReducement = min( - abs( tan( deg2rad( $this->angle ) ) * ( $labelSize / 2 ) ), - abs( $labelLength / 2 ) - ); - - switch ( true ) - { - case ( ( ( $degTextAngle >= 0 ) && - ( $degTextAngle < 90 ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) - ) - ) || - ( ( $degTextAngle >= 270 ) && - ( $degTextAngle < 360 ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) - ) - ) - ): - $labelBoundings = new ezcGraphBoundings( - $position->x, - $position->y, - $position->x + abs( $labelLength ) - $lengthReducement, - $position->y + $labelSize - ); - $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP; - $labelRotation = $degTextAngle; - break; - case ( ( ( $degTextAngle >= 90 ) && - ( $degTextAngle < 180 ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) - ) - ) || - ( ( $degTextAngle >= 180 ) && - ( $degTextAngle < 270 ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) - ) - ) - ): - $labelBoundings = new ezcGraphBoundings( - $position->x - abs( $labelLength ) + $lengthReducement, - $position->y, - $position->x, - $position->y + $labelSize - ); - $labelAlignement = ezcGraph::RIGHT | ezcGraph::TOP; - $labelRotation = $degTextAngle - 180; - break; - case ( ( ( $degTextAngle >= 180 ) && - ( $degTextAngle < 270 ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) - ) - ) || - ( ( $degTextAngle >= 90 ) && - ( $degTextAngle < 180 ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) - ) - ) - ): - $labelBoundings = new ezcGraphBoundings( - $position->x - abs( $labelLength ) + $lengthReducement, - $position->y - $labelSize, - $position->x, - $position->y - ); - $labelAlignement = ezcGraph::RIGHT | ezcGraph::BOTTOM; - $labelRotation = $degTextAngle - 180; - break; - case ( ( ( $degTextAngle >= 270 ) && - ( $degTextAngle < 360 ) && - ( ( $axis->position === ezcGraph::LEFT ) || - ( $axis->position === ezcGraph::RIGHT ) - ) - ) || - ( ( $degTextAngle >= 0 ) && - ( $degTextAngle < 90 ) && - ( ( $axis->position === ezcGraph::TOP ) || - ( $axis->position === ezcGraph::BOTTOM ) - ) - ) - ): - $labelBoundings = new ezcGraphBoundings( - $position->x, - $position->y + $labelSize, - $position->x + abs( $labelLength ) - $lengthReducement, - $position->y - ); - $labelAlignement = ezcGraph::LEFT | ezcGraph::BOTTOM; - $labelRotation = $degTextAngle; - break; - } - - $renderer->drawText( - $labelBoundings, - $step->label, - $labelAlignement, - new ezcGraphRotation( - $labelRotation, - $position - ) - ); - - // major grid - if ( $axis->majorGrid ) - { - $this->drawGrid( - $renderer, - $gridBoundings, - $position, - $stepSize, - $axis->majorGrid - ); - } - - // major step - $this->drawStep( - $renderer, - $position, - $this->direction, - $axis->position, - $this->majorStepSize, - $axis->border - ); - } - } - - /** - * Modify chart data position - * - * Optionally additionally modify the coodinate of a data point - * - * @param ezcGraphCoordinate $coordinate Data point coordinate - * @return ezcGraphCoordinate Modified coordinate - */ - public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) - { - return new ezcGraphCoordinate( - $coordinate->x * abs( $this->direction->y ) + - ( $coordinate->x * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->x ), - $coordinate->y * abs( $this->direction->x ) + - ( $coordinate->y * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->y ) - ); - } -} -?> + + * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRotatedLabelRenderer(); + * + * // Define angle manually in degree + * $chart->xAxis->axisLabelRenderer->angle = 45; + * + * // Increase axis space + * $chart->xAxis->axisSpace = .2; + * + * + * @property float $angle + * Angle of labels on axis in degrees. + * + * @version 1.4.3 + * @package Graph + * @mainclass + */ +class ezcGraphAxisRotatedLabelRenderer extends ezcGraphAxisLabelRenderer +{ + /** + * Store step array for later coordinate modifications + * + * @var array(ezcGraphStep) + */ + protected $steps; + + /** + * Store direction for later coordinate modifications + * + * @var ezcGraphVector + */ + protected $direction; + + /** + * Store coordinate width modifier for later coordinate modifications + * + * @var float + */ + protected $widthModifier; + + /** + * Store coordinate offset for later coordinate modifications + * + * @var float + */ + protected $offset; + + /** + * Constructor + * + * @param array $options Default option array + * @return void + * @ignore + */ + public function __construct( array $options = array() ) + { + parent::__construct( $options ); + $this->properties['angle'] = null; + $this->properties['labelOffset'] = true; + } + + /** + * __set + * + * @param mixed $propertyName + * @param mixed $propertyValue + * @throws ezcBaseValueException + * If a submitted parameter was out of range or type. + * @throws ezcBasePropertyNotFoundException + * If a the value for the property options is not an instance of + * @return void + * @ignore + */ + public function __set( $propertyName, $propertyValue ) + { + switch ( $propertyName ) + { + case 'angle': + if ( !is_numeric( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 360' ); + } + + $reducement = (int) ( $propertyValue - $propertyValue % 360 ); + $this->properties['angle'] = (float) $propertyValue - $reducement; + break; + + case 'labelOffset': + if ( !is_bool( $propertyValue ) ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); + } + + $this->properties[$propertyName] = (bool) $propertyValue; + break; + + default: + return parent::__set( $propertyName, $propertyValue ); + } + } + + /** + * Render Axis labels + * + * Render labels for an axis. + * + * @param ezcGraphRenderer $renderer Renderer used to draw the chart + * @param ezcGraphBoundings $boundings Boundings of the axis + * @param ezcGraphCoordinate $start Axis starting point + * @param ezcGraphCoordinate $end Axis ending point + * @param ezcGraphChartElementAxis $axis Axis instance + * @return void + */ + public function renderLabels( + ezcGraphRenderer $renderer, + ezcGraphBoundings $boundings, + ezcGraphCoordinate $start, + ezcGraphCoordinate $end, + ezcGraphChartElementAxis $axis, + ezcGraphBoundings $innerBoundings = null ) + { + // receive rendering parameters from axis + $steps = $axis->getSteps(); + $this->steps = $steps; + + $axisBoundings = new ezcGraphBoundings( + $start->x, $start->y, + $end->x, $end->y + ); + + // Determine normalized axis direction + $this->direction = new ezcGraphVector( + $end->x - $start->x, + $end->y - $start->y + ); + $this->direction->unify(); + $axisAngle = -$this->direction->angle( new ezcGraphVector( 1, 0 ) ); + + // Get axis space + $gridBoundings = null; + list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings ); + + // Determine optimal angle if none specified + if ( $this->angle === null ) + { + $minimumStepWidth = null; + foreach ( $steps as $nr => $step ) + { + if ( ( $minimumStepWidth === null ) || + ( $step->width < $minimumStepWidth ) ) + { + $minimumStepWidth = $step->width; + } + } + + $width = abs( + $axisBoundings->width * $minimumStepWidth * $this->direction->x + + $axisBoundings->height * $minimumStepWidth * $this->direction->y + ); + $height = abs( + $ySpace * $this->direction->x + + $xSpace * $this->direction->y + ); + + $length = sqrt( pow( $width, 2 ) + pow( $height, 2 ) ); + $this->angle = rad2deg( acos( $height / $length ) ); + } + + // Determine additional required axis space by boxes + $firstStep = reset( $steps ); + $lastStep = end( $steps ); + + $textAngle = $axisAngle + + deg2rad( $this->angle ) + + ( $axis->position & ( ezcGraph::TOP | ezcGraph::BOTTOM ) ? deg2rad( 270 ) : deg2rad( 90 ) ); + + // Ensure angle between 0 and 360 degrees + $degTextAngle = rad2deg( $textAngle ); + while ( $degTextAngle < 0 ) + { + $degTextAngle += 360.; + } + + if ( $this->properties['labelOffset'] ) + { + $this->offset = + ( $this->angle < 0 ? -1 : 1 ) * + ( $axis->position & ( ezcGraph::TOP | ezcGraph::LEFT ) ? 1 : -1 ) * + ( 1 - cos( deg2rad( $this->angle * 2 ) ) ); + } + else + { + $this->offset = 0; + } + + $axisSpaceFactor = abs( + ( $this->direction->x == 0 ? 0 : + $this->direction->x * $ySpace / $axisBoundings->width ) + + ( $this->direction->y == 0 ? 0 : + $this->direction->y * $xSpace / $axisBoundings->height ) + ); + + $start = new ezcGraphCoordinate( + $start->x + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ), + $start->y + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y ) + ); + $end = new ezcGraphCoordinate( + $end->x + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ), + $end->y + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y ) + ); + + $labelLength = sqrt( + pow( + $xSpace * $this->direction->y + + $axisSpaceFactor * $this->offset * ( $end->x - $start->x ), + 2 ) + + pow( + $ySpace * $this->direction->x + + $axisSpaceFactor * $this->offset * ( $end->y - $start->y ), + 2 ) + ); + + $this->offset *= $axisSpaceFactor; + + // Draw steps and grid + foreach ( $steps as $nr => $step ) + { + $position = new ezcGraphCoordinate( + $start->x + ( $end->x - $start->x ) * $step->position * abs( $this->direction->x ), + $start->y + ( $end->y - $start->y ) * $step->position * abs( $this->direction->y ) + ); + + $stepSize = new ezcGraphCoordinate( + ( $end->x - $start->x ) * $step->width, + ( $end->y - $start->y ) * $step->width + ); + + // Calculate label boundings + switch ( true ) + { + case ( $nr === 0 ): + $labelSize = min( + abs( + $xSpace * 2 * $this->direction->y + + $ySpace * 2 * $this->direction->x ), + abs( + $step->width * $axisBoundings->width * $this->direction->x + + $step->width * $axisBoundings->height * $this->direction->y ) + ); + break; + case ( $step->isLast ): + $labelSize = min( + abs( + $xSpace * 2 * $this->direction->y + + $ySpace * 2 * $this->direction->x ), + abs( + $steps[$nr - 1]->width * $axisBoundings->width * $this->direction->x + + $steps[$nr - 1]->width * $axisBoundings->height * $this->direction->y ) + ); + break; + default: + $labelSize = abs( + $step->width * $axisBoundings->width * $this->direction->x + + $step->width * $axisBoundings->height * $this->direction->y + ); + break; + } + + $labelSize = $labelSize * cos( deg2rad( $this->angle ) ); + $lengthReducement = min( + abs( tan( deg2rad( $this->angle ) ) * ( $labelSize / 2 ) ), + abs( $labelLength / 2 ) + ); + + switch ( true ) + { + case ( ( ( $degTextAngle >= 0 ) && + ( $degTextAngle < 90 ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) + ) + ) || + ( ( $degTextAngle >= 270 ) && + ( $degTextAngle < 360 ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) + ) + ) + ): + $labelBoundings = new ezcGraphBoundings( + $position->x, + $position->y, + $position->x + abs( $labelLength ) - $lengthReducement, + $position->y + $labelSize + ); + $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP; + $labelRotation = $degTextAngle; + break; + case ( ( ( $degTextAngle >= 90 ) && + ( $degTextAngle < 180 ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) + ) + ) || + ( ( $degTextAngle >= 180 ) && + ( $degTextAngle < 270 ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) + ) + ) + ): + $labelBoundings = new ezcGraphBoundings( + $position->x - abs( $labelLength ) + $lengthReducement, + $position->y, + $position->x, + $position->y + $labelSize + ); + $labelAlignement = ezcGraph::RIGHT | ezcGraph::TOP; + $labelRotation = $degTextAngle - 180; + break; + case ( ( ( $degTextAngle >= 180 ) && + ( $degTextAngle < 270 ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) + ) + ) || + ( ( $degTextAngle >= 90 ) && + ( $degTextAngle < 180 ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) + ) + ) + ): + $labelBoundings = new ezcGraphBoundings( + $position->x - abs( $labelLength ) + $lengthReducement, + $position->y - $labelSize, + $position->x, + $position->y + ); + $labelAlignement = ezcGraph::RIGHT | ezcGraph::BOTTOM; + $labelRotation = $degTextAngle - 180; + break; + case ( ( ( $degTextAngle >= 270 ) && + ( $degTextAngle < 360 ) && + ( ( $axis->position === ezcGraph::LEFT ) || + ( $axis->position === ezcGraph::RIGHT ) + ) + ) || + ( ( $degTextAngle >= 0 ) && + ( $degTextAngle < 90 ) && + ( ( $axis->position === ezcGraph::TOP ) || + ( $axis->position === ezcGraph::BOTTOM ) + ) + ) + ): + $labelBoundings = new ezcGraphBoundings( + $position->x, + $position->y + $labelSize, + $position->x + abs( $labelLength ) - $lengthReducement, + $position->y + ); + $labelAlignement = ezcGraph::LEFT | ezcGraph::BOTTOM; + $labelRotation = $degTextAngle; + break; + } + + $renderer->drawText( + $labelBoundings, + $step->label, + $labelAlignement, + new ezcGraphRotation( + $labelRotation, + $position + ) + ); + + // major grid + if ( $axis->majorGrid ) + { + $this->drawGrid( + $renderer, + $gridBoundings, + $position, + $stepSize, + $axis->majorGrid + ); + } + + // major step + $this->drawStep( + $renderer, + $position, + $this->direction, + $axis->position, + $this->majorStepSize, + $axis->border + ); + } + } + + /** + * Modify chart data position + * + * Optionally additionally modify the coodinate of a data point + * + * @param ezcGraphCoordinate $coordinate Data point coordinate + * @return ezcGraphCoordinate Modified coordinate + */ + public function modifyChartDataPosition( ezcGraphCoordinate $coordinate ) + { + return new ezcGraphCoordinate( + $coordinate->x * abs( $this->direction->y ) + + ( $coordinate->x * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->x ), + $coordinate->y * abs( $this->direction->x ) + + ( $coordinate->y * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->y ) + ); + } +} +?> diff --git a/library/ezc/Graph/src/structs/context.php b/library/ezc/Graph/src/structs/context.php index 08f790dd32..ab3fa72b98 100644 --- a/library/ezc/Graph/src/structs/context.php +++ b/library/ezc/Graph/src/structs/context.php @@ -1,92 +1,92 @@ -dataset = $dataset; - $this->datapoint = $datapoint; - $this->url = $url; - } - - /** - * __set_state - * - * @param array $properties Struct properties - * @return void - * @ignore - */ - public function __set_state( array $properties ) - { - $this->dataset = (string) $properties['dataset']; - $this->datapoint = (string) $properties['datapoint']; - - // Check to keep BC - // @TODO: Remvove unnesecary check on next major version - if ( array_key_exists( 'url', $properties ) ) - { - $this->url = (string) $properties['url']; - } - } -} - -?> +dataset = $dataset; + $this->datapoint = $datapoint; + $this->url = $url; + } + + /** + * __set_state + * + * @param array $properties Struct properties + * @return void + * @ignore + */ + public function __set_state( array $properties ) + { + $this->dataset = (string) $properties['dataset']; + $this->datapoint = (string) $properties['datapoint']; + + // Check to keep BC + // @TODO: Remvove unnesecary check on next major version + if ( array_key_exists( 'url', $properties ) ) + { + $this->url = (string) $properties['url']; + } + } +} + +?> diff --git a/library/ezc/Graph/src/structs/coordinate.php b/library/ezc/Graph/src/structs/coordinate.php index 31cfe689be..0af65b7ec5 100644 --- a/library/ezc/Graph/src/structs/coordinate.php +++ b/library/ezc/Graph/src/structs/coordinate.php @@ -1,76 +1,76 @@ -x = $x; - $this->y = $y; - } - - /** - * __set_state - * - * @param array $properties Struct properties - * @return void - * @ignore - */ - public function __set_state( array $properties ) - { - $this->x = $properties['x']; - $this->y = $properties['y']; - } - - /** - * Returns simple string representation of coordinate - * - * @return string - * @ignore - */ - public function __toString() - { - return sprintf( '( %.2f, %.2f )', $this->x, $this->y ); - } -} - -?> +x = $x; + $this->y = $y; + } + + /** + * __set_state + * + * @param array $properties Struct properties + * @return void + * @ignore + */ + public function __set_state( array $properties ) + { + $this->x = $properties['x']; + $this->y = $properties['y']; + } + + /** + * Returns simple string representation of coordinate + * + * @return string + * @ignore + */ + public function __toString() + { + return sprintf( '( %.2f, %.2f )', $this->x, $this->y ); + } +} + +?> diff --git a/library/ezc/Graph/src/structs/step.php b/library/ezc/Graph/src/structs/step.php index aad9f77210..546e375401 100644 --- a/library/ezc/Graph/src/structs/step.php +++ b/library/ezc/Graph/src/structs/step.php @@ -1,106 +1,106 @@ -position = (float) $position; - $this->width = (float) $width; - $this->label = $label; - $this->childs = $childs; - $this->isZero = (bool) $isZero; - $this->isLast = (bool) $isLast; - } - - /** - * __set_state - * - * @param array $properties Struct properties - * @return void - * @ignore - */ - public function __set_state( array $properties ) - { - $this->position = $properties['position']; - $this->width = $properties['width']; - $this->label = $properties['label']; - $this->childs = $properties['childs']; - $this->isZero = $properties['isZero']; - $this->isLast = $properties['isLast']; - } -} - -?> +position = (float) $position; + $this->width = (float) $width; + $this->label = $label; + $this->childs = $childs; + $this->isZero = (bool) $isZero; + $this->isLast = (bool) $isLast; + } + + /** + * __set_state + * + * @param array $properties Struct properties + * @return void + * @ignore + */ + public function __set_state( array $properties ) + { + $this->position = $properties['position']; + $this->width = $properties['width']; + $this->label = $properties['label']; + $this->childs = $properties['childs']; + $this->isZero = $properties['isZero']; + $this->isLast = $properties['isLast']; + } +} + +?> diff --git a/library/ezc/Graph/src/tools.php b/library/ezc/Graph/src/tools.php index 218ece1d28..8c343850e9 100644 --- a/library/ezc/Graph/src/tools.php +++ b/library/ezc/Graph/src/tools.php @@ -1,183 +1,183 @@ -driver instanceof ezcGraphGdDriver ) ) - { - throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphGdDriver' ); - } - - $elements = $chart->renderer->getElementReferences(); - - if ( !count( $elements ) ) - { - throw new ezcGraphToolsNotRenderedException( $chart ); - } - - $imageMap = sprintf( "\n", $name ); - - // Iterate over legends elements - if ( isset( $elements['legend'] ) ) - { - foreach ( $elements['legend'] as $objectName => $polygones ) - { - $url = $elements['legend_url'][$objectName]; - - if ( empty( $url ) ) - { - continue; - } - - foreach ( $polygones as $shape => $polygone ) - { - $coordinateString = ''; - foreach ( $polygone as $coordinate ) - { - $coordinateString .= sprintf( '%d,%d,', $coordinate->x, $coordinate->y ); - } - - $imageMap .= sprintf( "\t\"%s\"\n", - substr( $coordinateString, 0, -1 ), - $url, - $objectName - ); - } - } - } - - // Iterate over data - foreach ( $elements['data'] as $dataset => $datapoints ) - { - foreach ( $datapoints as $datapoint => $polygones ) - { - $url = $chart->data[$dataset]->url[$datapoint]; - - if ( empty( $url ) ) - { - continue; - } - - foreach ( $polygones as $polygon ) - { - $coordinateString = ''; - foreach ( $polygon as $coordinate ) - { - $coordinateString .= sprintf( '%d,%d,', $coordinate->x, $coordinate->y ); - } - - $imageMap .= sprintf( "\t\"%s\"\n", - substr( $coordinateString, 0, -1 ), - $url, - $datapoint - ); - } - } - } - - return $imageMap . "\n"; - } - - /** - * Add links to clickable SVG elements in a chart with SVG driver. - * - * @param ezcGraphChart $chart - * @return void - */ - public static function linkSvgElements( ezcGraphChart $chart ) - { - if ( ! ( $chart->driver instanceof ezcGraphSvgDriver ) ) - { - throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphSvgDriver' ); - } - - $fileName = $chart->getRenderedFile(); - - if ( !$fileName ) - { - throw new ezcGraphToolsNotRenderedException( $chart ); - } - - $dom = new DOMDocument(); - $dom->load( $fileName ); - $xpath = new DomXPath( $dom ); - - $elements = $chart->renderer->getElementReferences(); - - // Link chart elements - foreach ( $elements['data'] as $dataset => $datapoints ) - { - foreach ( $datapoints as $datapoint => $ids ) - { - $url = $chart->data[$dataset]->url[$datapoint]; - - if ( empty( $url ) ) - { - continue; - } - - foreach ( $ids as $id ) - { - $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 ); - - $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' ); - $element->setAttribute( 'onclick', "top.location = '{$url}'" ); - } - } - } - - // Link legend elements - if ( isset( $elements['legend'] ) ) - { - foreach ( $elements['legend'] as $objectName => $ids ) - { - $url = $elements['legend_url'][$objectName]; - - if ( empty( $url ) ) - { - continue; - } - - foreach ( $ids as $id ) - { - $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 ); - - $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' ); - $element->setAttribute( 'onclick', "top.location = '{$url}'" ); - } - } - } - - $dom->save( $fileName ); - } -} - -?> +driver instanceof ezcGraphGdDriver ) ) + { + throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphGdDriver' ); + } + + $elements = $chart->renderer->getElementReferences(); + + if ( !count( $elements ) ) + { + throw new ezcGraphToolsNotRenderedException( $chart ); + } + + $imageMap = sprintf( "\n", $name ); + + // Iterate over legends elements + if ( isset( $elements['legend'] ) ) + { + foreach ( $elements['legend'] as $objectName => $polygones ) + { + $url = $elements['legend_url'][$objectName]; + + if ( empty( $url ) ) + { + continue; + } + + foreach ( $polygones as $shape => $polygone ) + { + $coordinateString = ''; + foreach ( $polygone as $coordinate ) + { + $coordinateString .= sprintf( '%d,%d,', $coordinate->x, $coordinate->y ); + } + + $imageMap .= sprintf( "\t\"%s\"\n", + substr( $coordinateString, 0, -1 ), + $url, + $objectName + ); + } + } + } + + // Iterate over data + foreach ( $elements['data'] as $dataset => $datapoints ) + { + foreach ( $datapoints as $datapoint => $polygones ) + { + $url = $chart->data[$dataset]->url[$datapoint]; + + if ( empty( $url ) ) + { + continue; + } + + foreach ( $polygones as $polygon ) + { + $coordinateString = ''; + foreach ( $polygon as $coordinate ) + { + $coordinateString .= sprintf( '%d,%d,', $coordinate->x, $coordinate->y ); + } + + $imageMap .= sprintf( "\t\"%s\"\n", + substr( $coordinateString, 0, -1 ), + $url, + $datapoint + ); + } + } + } + + return $imageMap . "\n"; + } + + /** + * Add links to clickable SVG elements in a chart with SVG driver. + * + * @param ezcGraphChart $chart + * @return void + */ + public static function linkSvgElements( ezcGraphChart $chart ) + { + if ( ! ( $chart->driver instanceof ezcGraphSvgDriver ) ) + { + throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphSvgDriver' ); + } + + $fileName = $chart->getRenderedFile(); + + if ( !$fileName ) + { + throw new ezcGraphToolsNotRenderedException( $chart ); + } + + $dom = new DOMDocument(); + $dom->load( $fileName ); + $xpath = new DomXPath( $dom ); + + $elements = $chart->renderer->getElementReferences(); + + // Link chart elements + foreach ( $elements['data'] as $dataset => $datapoints ) + { + foreach ( $datapoints as $datapoint => $ids ) + { + $url = $chart->data[$dataset]->url[$datapoint]; + + if ( empty( $url ) ) + { + continue; + } + + foreach ( $ids as $id ) + { + $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 ); + + $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' ); + $element->setAttribute( 'onclick', "top.location = '{$url}'" ); + } + } + } + + // Link legend elements + if ( isset( $elements['legend'] ) ) + { + foreach ( $elements['legend'] as $objectName => $ids ) + { + $url = $elements['legend_url'][$objectName]; + + if ( empty( $url ) ) + { + continue; + } + + foreach ( $ids as $id ) + { + $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 ); + + $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' ); + $element->setAttribute( 'onclick', "top.location = '{$url}'" ); + } + } + } + + $dom->save( $fileName ); + } +} + +?> diff --git a/library/ezc/autoload/base_autoload.php b/library/ezc/autoload/base_autoload.php index 4bb3379318..bf631e383b 100644 --- a/library/ezc/autoload/base_autoload.php +++ b/library/ezc/autoload/base_autoload.php @@ -2,9 +2,9 @@ /** * Autoloader definition for the Base component. * - * @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved. + * @copyright Copyright (C) 2005-2009 eZ systems as. All rights reserved. * @license http://ez.no/licenses/new_bsd New BSD License - * @version 1.6rc1 + * @version 1.7 * @filesource * @package Base */ @@ -37,6 +37,9 @@ 'ezcBaseFile' => 'Base/file.php', 'ezcBaseFileFindContext' => 'Base/structs/file_find_context.php', 'ezcBaseInit' => 'Base/init.php', + 'ezcBaseMetaData' => 'Base/metadata.php', + 'ezcBaseMetaDataPearReader' => 'Base/metadata/pear.php', + 'ezcBaseMetaDataTarballReader' => 'Base/metadata/tarball.php', 'ezcBasePersistable' => 'Base/interfaces/persistable.php', 'ezcBaseRepositoryDirectory' => 'Base/structs/repository_directory.php', ); diff --git a/library/ezc/autoload/graph_autoload.php b/library/ezc/autoload/graph_autoload.php index b7119f0fa3..ecae1249f6 100644 --- a/library/ezc/autoload/graph_autoload.php +++ b/library/ezc/autoload/graph_autoload.php @@ -2,9 +2,9 @@ /** * Autoloader definition for the Graph component. * - * @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved. + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. * @license http://ez.no/licenses/new_bsd New BSD License - * @version 1.4rc1 + * @version 1.4.3 * @filesource * @package Graph */