diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/colors/color.php (renamed from src/structs/color.php) | 102 | ||||
-rw-r--r-- | src/colors/linear_gradient.php | 152 | ||||
-rw-r--r-- | src/colors/radial_gradient.php | 158 | ||||
-rw-r--r-- | src/driver/gd.php | 97 | ||||
-rw-r--r-- | src/driver/svg.php | 392 | ||||
-rw-r--r-- | src/graph_autoload.php | 4 | ||||
-rw-r--r-- | src/interfaces/driver.php | 2 | ||||
-rw-r--r-- | src/options/driver.php | 6 | ||||
-rw-r--r-- | src/options/gd_driver.php | 5 | ||||
-rw-r--r-- | src/options/renderer_3d.php | 35 | ||||
-rw-r--r-- | src/options/svg_driver.php | 20 | ||||
-rw-r--r-- | src/renderer/3d.php | 145 |
12 files changed, 890 insertions, 228 deletions
diff --git a/src/structs/color.php b/src/colors/color.php index 99d9fde..9784388 100644 --- a/src/structs/color.php +++ b/src/colors/color.php @@ -1,4 +1,12 @@ <?php +/** + * File containing the ezcGraphColor class + * + * @package Graph + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ /** * ezcGraphColor @@ -6,67 +14,63 @@ * Struct for representing colors in ezcGraph. A color is defined using the * common RGBA model with integer values between 0 and 255. An alpha value * of zero means full opacity, while 255 means full transparency. + * + * @property integer $red + * Red RGBA value of color. + * @property integer $green + * Green RGBA value of color. + * @property integer $blue + * Blue RGBA value of color. + * @property integer $alpha + * Alpha RGBA value of color. + * + * @package Graph */ -class ezcGraphColor +class ezcGraphColor extends ezcBaseOptions { /** - * Red color value. - * - * Contains a value between 0 and 255 - * - * @var integer - */ - public $red = 0; - - /** - * Green color value. - * - * Contains a value between 0 and 255 - * - * @var integer - */ - public $green = 0; - - /** - * Blue color value. - * - * Contains a value between 0 and 255 + * Constructor * - * @var integer + * @param array $options Default option array + * @return void + * @ignore */ - public $blue = 0; - - /** - * Alpha color value. - * - * Contains a value between 0 and 255. 0 means full opacity and 255 full - * transparency. - * - * @var integer - */ - public $alpha = 0; - - /** - * Empty constructor - */ - public function __construct() + public function __construct( array $options = array() ) { - } + $this->properties['red'] = 0; + $this->properties['green'] = 0; + $this->properties['blue'] = 0; + $this->properties['alpha'] = 0; - /** - * Throws a BasePropertyNotFound exception. - */ - public function __set( $name, $key ) - { - throw new ezcBasePropertyNotFoundException( $name ); + parent::__construct( $options ); } /** - * Throws a BasePropertyNotFound exception. + * __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 __get( $name ) + public function __set( $propertyName, $propertyValue ) { - throw new ezcBasePropertyNotFoundException( $name ); + switch ( $propertyName ) + { + case 'red': + case 'green': + case 'blue': + case 'alpha': + $this->properties[$propertyName] = max( 0, min( 255, (int) $propertyValue ) ); + break; + default: + throw new ezcGraphNoSuchDataSetException( $propertyName ); + break; + } } /** diff --git a/src/colors/linear_gradient.php b/src/colors/linear_gradient.php new file mode 100644 index 0000000..3bbde96 --- /dev/null +++ b/src/colors/linear_gradient.php @@ -0,0 +1,152 @@ +<?php +/** + * File containing the ezcGraphLinearGradient class + * + * @package Graph + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Class representing linear gradient fills. For drivers which cannot draw + * gradients it falls back to a native ezcGraphColor + * + * @property ezcGraphCoordinate $startPoint + * Starting point of the gradient. + * @property ezcGraphCoordinate $endPoint + * Ending point of the gradient. + * @property ezcGraphColor $startColor + * Starting color of the gradient. + * @property ezcGraphColor $endColor + * Ending color of the gradient. + * + * @package Graph + */ +class ezcGraphLinearGradient extends ezcGraphColor +{ + /** + * Constructor + * + * @param ezcGraphCoordinate $startPoint + * @param ezcGraphCoordinate $endPoint + * @param ezcGraphColor $startColor + * @param ezcGraphColor $endColor + * @return void + */ + public function __construct( ezcGraphCoordinate $startPoint, ezcGraphCoordinate $endPoint, ezcGraphColor $startColor, ezcGraphColor $endColor ) + { + $this->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': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['startColor'] = $propertyValue; + } + break; + case 'endColor': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['endColor'] = $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 ); + } + } + } + + 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/src/colors/radial_gradient.php b/src/colors/radial_gradient.php new file mode 100644 index 0000000..78b22ca --- /dev/null +++ b/src/colors/radial_gradient.php @@ -0,0 +1,158 @@ +<?php +/** + * File containing the ezcGraphRadialGradient class + * + * @package Graph + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Class representing linear gradient fills. For drivers which cannot draw + * gradients it falls back to a native ezcGraphColor + * + * @property ezcGraphCoordinate $center + * Center point of the gradient. + * @property int $width + * Width of ellipse + * @property int $height + * Width of ellipse + * @property int $offset + * Offset for starting color + * @property ezcGraphColor $startColor + * Starting color of the gradient. + * @property ezcGraphColor $endColor + * Ending color of the gradient. + * + * @package Graph + */ +class ezcGraphRadialGradient extends ezcGraphColor +{ + /** + * Constructor + * + * @param ezcGraphCoordinate $startPoint + * @param ezcGraphCoordinate $endPoint + * @param ezcGraphColor $startColor + * @param ezcGraphColor $endColor + * @return void + */ + public function __construct( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $startColor, ezcGraphColor $endColor ) + { + $this->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': + $this->properties['width'] = (float) $propertyValue; + break; + case 'height': + $this->properties['height'] = (float) $propertyValue; + break; + case 'offset': + $this->properties['offset'] = min( 0, max( 1, (float) $propertyValue ) ); + break; + case 'startColor': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['startColor'] = $propertyValue; + } + break; + case 'endColor': + if ( !$propertyValue instanceof ezcGraphCoordinate ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' ); + } + else + { + $this->properties['endColor'] = $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 ); + } + } + } + + 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/src/driver/gd.php b/src/driver/gd.php index ff8a5db..c4770ce 100644 --- a/src/driver/gd.php +++ b/src/driver/gd.php @@ -473,7 +473,7 @@ class ezcGraphGdDriver extends ezcGraphDriver ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) ), ), - $color->darken( $this->options->shadeCircularArc * abs ( cos ( deg2rad( $startAngle ) ) ) ), + $color->darken( $this->options->shadeCircularArc * ( 1 + cos ( deg2rad( $startAngle ) ) ) / 2 ), true ); } @@ -490,7 +490,7 @@ class ezcGraphGdDriver extends ezcGraphDriver * @param ezcGraphColor $color Color of Border * @return void */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color ) + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) { $image = $this->getImage(); $drawColor = $this->allocate( $color ); @@ -502,49 +502,66 @@ class ezcGraphGdDriver extends ezcGraphDriver $startAngle = $endAngle; $endAngle = $tmp; } - - $startIteration = ceil( $startAngle / $this->options->detail ) * $this->options->detail; - $endIteration = floor( $endAngle / $this->options->detail ) * $this->options->detail; - - if ( $startAngle < $startIteration ) + + if ( $filled === true ) { - // Draw initial step - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $startAngle, - $startIteration, - $color - ); - } + $startIteration = ceil( $startAngle / $this->options->detail ) * $this->options->detail; + $endIteration = floor( $endAngle / $this->options->detail ) * $this->options->detail; - // Draw all steps - for ( ; $startIteration < $endIteration; $startIteration += $this->options->detail ) - { - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $startIteration, - $startIteration + $this->options->detail, - $color - ); - } + if ( $startAngle < $startIteration ) + { + // Draw initial step + $this->drawCircularArcStep( + $center, + $width, + $height, + $size, + $startAngle, + $startIteration, + $color + ); + } - if ( $endIteration < $endAngle ) + // 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 { - // Draw closing step - $this->drawCircularArcStep( - $center, - $width, - $height, - $size, - $endIteration, + imagefilledarc( + $image, + $this->supersample( $center->x ), + $this->supersample( $center->y ), + $this->supersample( $width ), + $this->supersample( $height ), + $startAngle, $endAngle, - $color + $drawColor, + IMG_ARC_PIE | IMG_ARC_NOFILL ); } } diff --git a/src/driver/svg.php b/src/driver/svg.php index 51f356a..0a6bd28 100644 --- a/src/driver/svg.php +++ b/src/driver/svg.php @@ -48,6 +48,13 @@ class ezcGraphSvgDriver extends ezcGraphDriver */ protected $strings = array(); + /** + * List of already created gradients + * + * @var array + */ + protected $drawnGradients = array(); + public function __construct( array $options = array() ) { $this->options = new ezcGraphSvgDriverOptions( $options ); @@ -104,6 +111,158 @@ class ezcGraphSvgDriver extends ezcGraphDriver } } + 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', $color->startPoint->x ); + $gradient->setAttribute( 'y1', $color->startPoint->y ); + $gradient->setAttribute( 'x2', $color->endPoint->x ); + $gradient->setAttribute( 'y2', $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', $color->center->x ); + $gradient->setAttribute( 'cy', $color->center->y ); + $gradient->setAttribute( 'fx', $color->center->x ); + $gradient->setAttribute( 'fy', $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; + } + + } + + 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 * @@ -134,32 +293,10 @@ class ezcGraphSvgDriver extends ezcGraphDriver $path = $this->dom->createElement( 'path' ); $path->setAttribute( 'd', $pointString ); - if ( $filled ) - { - $path->setAttribute( - 'style', - sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2f; stroke: none;', - $color->red, - $color->green, - $color->blue, - 1 - ( $color->alpha / 255 ) - ) - ); - } - else - { - $path->setAttribute( - 'style', - sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2f; stroke-linecap: %s;', - $color->red, - $color->green, - $color->blue, - $thickness, - 1 - ( $color->alpha / 255 ), - $this->options->strokeLineCap - ) - ); - } + $path->setAttribute( + 'style', + $this->getStyle( $color, $filled, $thickness ) + ); $this->elements->appendChild( $path ); } @@ -187,14 +324,7 @@ class ezcGraphSvgDriver extends ezcGraphDriver $path->setAttribute( 'd', $pointString ); $path->setAttribute( 'style', - sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2f; stroke-linecap: %s;', - $color->red, - $color->green, - $color->blue, - $thickness, - 1 - ( $color->alpha / 255 ), - $this->options->strokeLineCap - ) + $this->getStyle( $color, false, $thickness ) ); $this->elements->appendChild( $path ); @@ -402,7 +532,7 @@ class ezcGraphSvgDriver extends ezcGraphDriver $Yend = $center->y + $this->options->graphOffset->y + $height * sin( ( ( $endAngle ) / 180 ) * M_PI ); $arc = $this->dom->createElement( 'path' ); - $arc->setAttribute('d', sprintf('M %.2f,%.2f L %.2f,%.2f A %.2f,%2f 0 %d,1 %.2f,%.2f z', + $arc->setAttribute('d', sprintf('M %.2f,%.2f L %.2f,%.2f A %.2f,%.2f 0 %d,1 %.2f,%.2f z', // Middle $center->x + $this->options->graphOffset->x, $center->y + $this->options->graphOffset->y, // Startpoint @@ -416,32 +546,10 @@ class ezcGraphSvgDriver extends ezcGraphDriver ) ); - if ( $filled ) - { - $arc->setAttribute( - 'style', - sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2f; stroke: none;', - $color->red, - $color->green, - $color->blue, - 1 - ( $color->alpha / 255 ) - ) - ); - } - else - { - $arc->setAttribute( - 'style', - sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2f; stroke-linecap: %s;', - $color->red, - $color->green, - $color->blue, - 1, // Line Thickness - 1 - ( $color->alpha / 255 ), - $this->options->strokeLineCap - ) - ); - } + $arc->setAttribute( + 'style', + $this->getStyle( $color, $filled, 1 ) + ); $this->elements->appendChild( $arc ); } @@ -458,7 +566,7 @@ class ezcGraphSvgDriver extends ezcGraphDriver * @param ezcGraphColor $color Color of Border * @return void */ - public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color ) + public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ) { $this->createDocument(); @@ -470,50 +578,120 @@ class ezcGraphSvgDriver extends ezcGraphDriver $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( ( -$startAngle / 180 ) * M_PI ); + $Xstart = $center->x + $this->options->graphOffset->x + $width * cos( -( $startAngle / 180 ) * M_PI ); $Ystart = $center->y + $this->options->graphOffset->y + $height * sin( ( $startAngle / 180 ) * M_PI ); $Xend = $center->x + $this->options->graphOffset->x + $width * cos( ( -( $endAngle ) / 180 ) * M_PI ); $Yend = $center->y + $this->options->graphOffset->y + $height * sin( ( ( $endAngle ) / 180 ) * M_PI ); - $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 - ) - ); + 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', - sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2f; stroke: none;', - $color->red, - $color->green, - $color->blue, - 1 - ( $color->alpha / 255 ) - ) + $this->getStyle( $color, $filled ) ); $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 ) + ); + + $this->elements->appendChild( $arc ); + } } /** @@ -537,32 +715,10 @@ class ezcGraphSvgDriver extends ezcGraphDriver $ellipse->setAttribute( 'rx', $width / 2 ); $ellipse->setAttribute( 'ry', $height / 2 ); - if ( $filled ) - { - $ellipse->setAttribute( - 'style', - sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2f; stroke: none;', - $color->red, - $color->green, - $color->blue, - 1 - ( $color->alpha / 255 ) - ) - ); - } - else - { - $ellipse->setAttribute( - 'style', - sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2f; stroke-linecap: %s;', - $color->red, - $color->green, - $color->blue, - 1, // Line Thickness - 1 - ( $color->alpha / 255 ), - $this->options->strokeLineCap - ) - ); - } + $ellipse->setAttribute( + 'style', + $this->getStyle( $color, $filled, 1 ) + ); $this->elements->appendChild( $ellipse ); } diff --git a/src/graph_autoload.php b/src/graph_autoload.php index 0d45502..cc89d22 100644 --- a/src/graph_autoload.php +++ b/src/graph_autoload.php @@ -26,7 +26,9 @@ return array( 'ezcGraphChartDataContainer' => 'Graph/data_container/base.php', 'ezcGraphChartSingleDataContainer' => 'Graph/data_container/single.php', - 'ezcGraphColor' => 'Graph/structs/color.php', + 'ezcGraphColor' => 'Graph/colors/color.php', + 'ezcGraphLinearGradient' => 'Graph/colors/linear_gradient.php', + 'ezcGraphRadialGradient' => 'Graph/colors/radial_gradient.php', 'ezcGraphUnknownColorDefinitionException' => 'Graph/exceptions/unknown_color_definition.php', 'ezcGraphRenderer' => 'Graph/interfaces/renderer.php', diff --git a/src/interfaces/driver.php b/src/interfaces/driver.php index a653009..b7045f0 100644 --- a/src/interfaces/driver.php +++ b/src/interfaces/driver.php @@ -130,7 +130,7 @@ abstract class ezcGraphDriver * @param ezcGraphColor $color Color of Border * @return void */ - abstract public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color ); + abstract public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true ); /** * Draws a circle diff --git a/src/options/driver.php b/src/options/driver.php index 14d5ef9..2bc2c55 100644 --- a/src/options/driver.php +++ b/src/options/driver.php @@ -14,6 +14,8 @@ * Width of the chart. * @property int $height * Height of the chart. + * @property float $shadeCircularArc + * Percent to darken circular arcs at the sides * @property int $font * Font used in the graph. * @property float $lineSpacing @@ -33,6 +35,7 @@ abstract class ezcGraphDriverOptions extends ezcBaseOptions public function __construct( array $options = array() ) { $this->properties['lineSpacing'] = .1; + $this->properties['shadeCircularArc'] = .5; $this->properties['font'] = new ezcGraphFontOptions(); $this->properties['font']->color = ezcGraphColor::fromHex( '#000000' ); @@ -58,6 +61,9 @@ abstract class ezcGraphDriverOptions extends ezcBaseOptions case 'height': $this->properties['height'] = max( 1, (int) $propertyValue ); break; + case 'shadeCircularArc': + $this->properties['shadeCircularArc'] = max( 0, min( 1, (float) $propertyValue ) ); + break; case 'font': if ( $propertyValue instanceof ezcGraphFontOptions ) { diff --git a/src/options/gd_driver.php b/src/options/gd_driver.php index dc0a5fb..a6e756d 100644 --- a/src/options/gd_driver.php +++ b/src/options/gd_driver.php @@ -15,8 +15,6 @@ * Should be one of those: IMG_PNG, IMG_JPEG * @property int $detail * Count of degrees to render one polygon for in circular arcs - * @property float $shadeCircularArc - * Percent to darken circular arcs at the sides * @property int $supersampling * Factor of supersampling used to simulate antialiasing * @property string $background @@ -73,9 +71,6 @@ class ezcGraphGdDriverOptions extends ezcGraphDriverOptions case 'detail': $this->properties['detail'] = max( 1, (int) $propertyValue ); break; - case 'shadeCircularArc': - $this->properties['shadeCircularArc'] = max( 0, min( 1, (float) $propertyValue ) ); - break; case 'supersampling': $this->properties['supersampling'] = (int) max( 1, $propertyValue ); break; diff --git a/src/options/renderer_3d.php b/src/options/renderer_3d.php index 9adc703..9de4b03 100644 --- a/src/options/renderer_3d.php +++ b/src/options/renderer_3d.php @@ -24,8 +24,16 @@ * @property float $pieChartRotation * Rotation of pie chart. Defines the percent of width used to * calculate the height of the ellipse. - * @property float $pieChartShadow - * Used transparency for pie chart shadows + * @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 $pieChartGleam + * Enhance pie chart with gleam on top. + * @property float $pieChartGleamColor + * Color used for gleam on pie charts. * @property float $barDarkenSide * Factor to darken the color used for the bars side polygon. * @property float $barDarkenTop @@ -50,7 +58,11 @@ class ezcGraphRenderer3dOptions extends ezcGraphRendererOptions $this->properties['depth'] = .1; $this->properties['pieChartHeight'] = 10; $this->properties['pieChartRotation'] = .6; - $this->properties['pieChartShadow'] = .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; @@ -92,8 +104,21 @@ class ezcGraphRenderer3dOptions extends ezcGraphRendererOptions case 'pieChartRotation': $this->properties['pieChartRotation'] = min( 1, max( 0, (float) $propertyValue ) ); break; - case 'pieChartShadow': - $this->properties['pieChartShadow'] = min( 1, max( 0, (float) $propertyValue ) ); + case 'pieChartGleam': + $this->properties['pieChartGleam'] = min( 1, max( 0, (float) $propertyValue ) ); + break; + case 'pieChartShadowSize': + $this->properties['pieChartShadowSize'] = max( 0, (int) $propertyValue ); + break; + case 'pieChartShadowTransparency': + $this->properties['pieChartShadowTransparency'] = min( 1, max( 0, (float) $propertyValue ) ); + break; + case 'pieChartShadowColor': + if ( !$propertyValue instanceof ezcGraphColor ) + { + throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphColor' ); + } + $this->properties['pieChartShadowColor'] = $propertyValue; break; case 'barDarkenSide': $this->properties['barDarkenSide'] = min( 1, max( 0, (float) $propertyValue ) ); diff --git a/src/options/svg_driver.php b/src/options/svg_driver.php index e6b3339..81ed597 100644 --- a/src/options/svg_driver.php +++ b/src/options/svg_driver.php @@ -15,6 +15,8 @@ * @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 @@ -54,6 +56,7 @@ class ezcGraphSvgDriverOptions extends ezcGraphDriverOptions public function __construct( array $options = array() ) { $this->properties['assumedCharacterWidth'] = .55; + $this->properties['strokeLineJoin'] = 'round'; $this->properties['strokeLineCap'] = 'round'; $this->properties['shapeRendering'] = 'geometricPrecision'; $this->properties['colorRendering'] = 'optimizeQuality'; @@ -82,6 +85,23 @@ class ezcGraphSvgDriverOptions extends ezcGraphDriverOptions case 'assumedCharacterWidth': $this->properties['assumedCharacterWidth'] = min( 1, max( 0, (float) $propertyValue ) ); break; + case 'strokeLineJoin': + $values = array( + 'round', + 'butt', + 'square', + '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', diff --git a/src/renderer/3d.php b/src/renderer/3d.php index 50398bb..c1fefd2 100644 --- a/src/renderer/3d.php +++ b/src/renderer/3d.php @@ -110,11 +110,11 @@ class ezcGraphRenderer3d extends ezcGraphRenderer // Move pie segment out of the center if ( $moveOut ) { - $direction = $startAngle + ( $endAngle - $startAngle ) / 2; + $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 ) ) + $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) * $this->options->pieChartRotation ); } @@ -131,13 +131,13 @@ class ezcGraphRenderer3d extends ezcGraphRenderer if ( $label ) { // Determine position of label - $middle = $startAngle + ( $endAngle - $startAngle ) / 2; + $direction = ( $endAngle + $startAngle ) / 2; $pieSegmentCenter = new ezcGraphCoordinate( - cos( deg2rad( $middle ) ) * $radius * 2 / 3 + $center->x, - sin( deg2rad( $middle ) ) * $radius * $this->options->pieChartRotation * 2 / 3 + $center->y + $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3, + $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3 * $this->options->pieChartRotation ); - // Split labels up into left an right size and index them on their + // Split labels up into left a right site and index them on their // y position $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array( clone $pieSegmentCenter, @@ -271,9 +271,42 @@ class ezcGraphRenderer3d extends ezcGraphRenderer { $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 ); $zBuffer[ @@ -378,6 +411,38 @@ class ezcGraphRenderer3d extends ezcGraphRenderer ); } + 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 ) + { + $midAngle = ( $circleSector['start'] + $circleSector['end'] ) / 2; + $this->driver->drawCircleSector( + new ezcGraphCoordinate( + $circleSector['center']->x, + $circleSector['center']->y + $this->options->pieChartHeight + ), + $circleSector['width'] + $i * 2, + $circleSector['height'] + $i * 2, + $circleSector['start'] - ( $this->options->pieChartShadowSize - $i ), + $circleSector['end'] + ( $this->options->pieChartShadowSize - $i ), + $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), + true + ); + } + } + ksort( $zBuffer ); foreach ( $zBuffer as $sides ) { @@ -400,6 +465,29 @@ class ezcGraphRenderer3d extends ezcGraphRenderer 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->driver->drawCircleSector( + $circleSector['center'], + $circleSector['width'], + $circleSector['height'], + $circleSector['start'], + $circleSector['end'], + $gradient, + true + ); + } + $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); $this->driver->drawCircleSector( $circleSector['center'], @@ -410,6 +498,31 @@ class ezcGraphRenderer3d extends ezcGraphRenderer $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 + ); + } } } @@ -418,7 +531,7 @@ class ezcGraphRenderer3d extends ezcGraphRenderer foreach ( $this->frontLines as $line ) { $this->driver->drawLine( - $line[0], + $line[0], $line[1], $line[2], $line[3] @@ -652,10 +765,24 @@ class ezcGraphRenderer3d extends ezcGraphRenderer 'index' => $barCenterBottom->x + 1, 'method' => 'drawCircle', 'parameters' => array( - $this->get3dCoordinate( $barCenterTop, $midDepth ), + $top = $this->get3dCoordinate( $barCenterTop, $midDepth ), $barWidth, $barWidth / 2, - ( $symbol === ezcGraph::CIRCLE ? $color->darken( $this->options->barDarkenTop ) : $color ) + ( $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 + ) ), ); |