diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1fa683 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# phpstorm project files +.idea + +# netbeans project files +nbproject + +# zend studio for eclipse project files +.buildpath +.project +.settings + +# composer vendor dir +vendor +composer.lock + +# local phpunit config +phpunit.xml + +# log files +tests/logs \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..22494d2 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Jyotish Draw + +Drawing component for Jyotish Library. + +## System Requirements + +Jyotish Draw component requires PHP 5.6 or later and [Jyotish Library](https://github.com/kunjara/jyotish). + +## License + +The files in this library are released under the GNU General Public License version 2 or later. + +## Demo + +To test Jyotish Draw component, a demo website is online: [jyotish.su](http://jyotish.su), or visit social dating service [abboom.com](https://abboom.com) \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..34cae73 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "kunjara/jyotish-draw", + "description": "Drawing component for Jyotish Library", + "keywords": [ + "jyotish", "vedic astrology", "natal chart" + ], + "minimum-stability": "dev", + "type": "library", + "license": "GPL-2.0+", + "authors": [ + { + "name": "Kunjara Lila das", + "email": "vladya108@gmail.com", + "homepage": "http://web-art.su" + } + ], + "require": { + "php": ">=5.6", + "kunjara/jyotish": "1.0.*@dev" + }, + "require-dev": { + "phpunit/phpunit": "5.2.*" + }, + "autoload": { + "psr-4": { + "Jyotish\\Draw\\": "src" + } + } +} \ No newline at end of file diff --git a/src/Draw.php b/src/Draw.php new file mode 100644 index 0000000..4ed22ee --- /dev/null +++ b/src/Draw.php @@ -0,0 +1,109 @@ + + */ +class Draw +{ + /** + * Image renderer + */ + const RENDERER_IMAGE = 'image'; + /** + * Svg renderer + */ + const RENDERER_SVG = 'svg'; + + /** + * Renderer object. + * + * @var Image|Svg + */ + protected $Renderer = null; + + /** + * Constructor + * + * @param int $width Width of drawing + * @param int $height Height of drawing + * @param string $renderer Renderer name (optional) + * @throws Exception\UnexpectedValueException + */ + public function __construct($width, $height, $renderer = self::RENDERER_IMAGE) { + if (!in_array(strtolower($renderer), [self::RENDERER_IMAGE, self::RENDERER_SVG])) { + throw new Exception\UnexpectedValueException( + "Invalid renderer provided must be 'image' or 'svg'." + ); + } + + $rendererClass = 'Jyotish\Draw\Renderer\\' . ucfirst($renderer); + $this->Renderer = new $rendererClass($width, $height); + } + + /** + * Set options. + * + * @param array $options Options to set + * @return Draw + */ + public function setOptions($options) { + $this->Renderer->setOptions($options); + + return $this; + } + + /** + * Draw text. + * + * @param string $text + * @param int $x + * @param int $y + * @param null|array $options Options to set (optional) + */ + public function drawText($text, $x, $y, array $options = null) { + $this->Renderer->drawText($text, $x, $y, $options); + } + + /** + * Draw polygon. + * + * @param array $points An array containing the polygon's vertices. + * @param null|array $options Options to set (optional) + */ + public function drawPolygon(array $points, array $options = null) + { + $this->Renderer->drawPolygon($points, $options); + } + + /** + * Draw chakra. + * + * @param \Jyotish\Base\Data $Data + * @param int $x + * @param int $y + * @param null|array $options Options to set (optional) + */ + public function drawChakra(\Jyotish\Base\Data $Data, $x, $y, array $options = null) { + $this->setOptions($options); + + $ChakraRenderer = new \Jyotish\Draw\Plot\Chakra\Renderer($this->Renderer); + $ChakraRenderer->drawChakra($Data, $x, $y, $options); + } + + /** + * Render the drawing with correct headers. + * + * @return mixed + */ + public function render() { + $this->Renderer->render(); + } +} \ No newline at end of file diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php new file mode 100644 index 0000000..9d587fd --- /dev/null +++ b/src/Exception/ExceptionInterface.php @@ -0,0 +1,15 @@ + + */ +class Renderer +{ + use \Jyotish\Base\Traits\DataTrait; + use \Jyotish\Base\Traits\OptionTrait; + + /** + * Renderer object. + * + * @var Image|Svg + */ + protected $Renderer = null; + + /** + * Chakra object. + * + * @var North|South|East + */ + protected $Chakra = null; + + protected $optionChakraSize = 200; + protected $optionChakraStyle = Chakra::STYLE_NORTH; + protected $optionChakraVarga = Varga::KEY_D1; + + protected $optionOffsetBorder = 4; + protected $optionWidthOffsetLabel = 20; + protected $optionHeightOffsetLabel = 14; + + protected $optionLabelGrahaType = 0; + protected $optionLabelGrahaCallback = ''; + + protected $optionLabelRashiFont = ''; + protected $optionLabelGrahaFont = ''; + protected $optionLabelExtraFont = ''; + + /** + * Constructor + * + * @param Image|Svg $Renderer + */ + public function __construct($Renderer) + { + $this->Renderer = $Renderer; + } + + /** + * Draw chakra. + * + * @param \Jyotish\Base\Data $Data + * @param int $x + * @param int $y + * @param null|array $options Options to set (optional) + */ + public function drawChakra(\Jyotish\Base\Data $Data, $x, $y, array $options = null) + { + $this->setData($Data); + $this->setOptions($options); + + $chakraStyle = 'Jyotish\Draw\Plot\Chakra\Style\\' . ucfirst($this->optionChakraStyle); + $this->Chakra = new $chakraStyle($Data); + + $bhavaPoints = $this->Chakra->getBhavaPoints($this->optionChakraSize, $x, $y); + + foreach ($bhavaPoints as $number => $points) { + if ($this->optionChakraStyle == Chakra::STYLE_NORTH) { + $bhava = ' bhava'.$number; + $rashi = ' rashi'.$Data->getData()['bhava'][$number]['rashi']; + } else { + $rashi = ' rashi'.$number; + $Rashi = Rashi::getInstance($number); + $Rashi->setEnvironment($Data); + $bhava = ' bhava'.$Rashi->getBhava(); + } + + $attributes = [ + 'class' => 'bhava'.$bhava.$rashi, + ]; + + $options = array_merge($this->getOptions(), ['attributes' => $attributes]); + $this->Renderer->drawPolygon($points, $options); + } + + $this->drawRashiLabel($x, $y, $this->getOptions()); + + $this->drawBodyLabel($x, $y, $this->getOptions()); + } + + /** + * Draw rashi labels. + * + * @param int $x + * @param int $y + * @param null|array $options + */ + private function drawRashiLabel($x, $y, array $options = null) + { + if (isset($options['labelRashiFont'])) { + $this->Renderer->setOptions($options['labelRashiFont']); + } + + $rashiLabelPoints = $this->Chakra->getRashiLabelPoints($this->getOptions()); + foreach ($rashiLabelPoints as $rashi => $point) { + $this->Renderer->drawText( + $rashi, + $point['x'] + $x, + $point['y'] + $y, + ['textAlign' => $point['textAlign'], 'textValign' => $point['textValign']] + ); + } + } + + /** + * Draw body labels. + * + * @param int $x + * @param int $y + * @param null|array $options + */ + private function drawBodyLabel($x, $y, array $options = null) + { + if (isset($options['labelGrahaFont'])) { + $this->Renderer->setOptions($options['labelGrahaFont']); + } + + $bodyLabelPoints = $this->Chakra->getBodyLabelPoints($this->getOptions()); + + foreach ($bodyLabelPoints as $body => $point) { + if (!array_key_exists($body, Graha::$graha) && isset($options['labelExtraFont'])) { + $this->Renderer->setOptions($options['labelExtraFont']); + } + + $bodyLabel = $this->getBodyLabel($body, [ + 'labelGrahaType' => $this->optionLabelGrahaType, + 'labelGrahaCallback' => $this->optionLabelGrahaCallback + ]); + + $this->Renderer->drawText( + $bodyLabel, + $point['x'] + $x, + $point['y'] + $y, + ['textAlign' => $point['textAlign'], 'textValign' => $point['textValign']] + ); + } + } + + /** + * Return body label. + * + * @param string $body + * @param array $options + * @return string + */ + private function getBodyLabel($body, array $options) + { + switch ($options['labelGrahaType']) { + case 0: + $label = $body; + break; + case 1: + if (array_key_exists($body, Graha::$graha)) { + $grahaObject = Graha::getInstance($body); + $label = Utility::unicodeToHtml($grahaObject->grahaUnicode); + } else { + $label = $body; + } + break; + case 2: + $label = call_user_func($options['labelGrahaCallback'], $body); + break; + default: + $label = $body; + break; + } + + $data = $this->Data->getData(); + + if (array_key_exists($body, Graha::listGraha(Graha::LIST_SAPTA))) { + $vakraCheshta = $data['graha'][$body]['speed'] < 0 ? true : false; + } else { + $vakraCheshta = false; + } + + $grahaLabel = $vakraCheshta ? '(' . $label . ')' : $label; + + return $grahaLabel; + } + + /** + * Set chakra varga. + * + * @param string $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\UnexpectedValueException + */ + public function setOptionChakraVarga($value) + { + $valueUcf = ucfirst($value); + if (!array_key_exists($valueUcf, Varga::$varga)) { + throw new Exception\UnexpectedValueException( + 'Varga key is wrong.' + ); + } + $this->optionChakraVarga = $valueUcf; + return $this; + } + + /** + * Set chakra size. Chakra size should be greater than or equals 100. + * + * @param int $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\OutOfRangeException + */ + public function setOptionChakraSize($value) + { + if (!is_numeric($value) || intval($value) < 100) { + throw new Exception\OutOfRangeException( + 'Chakra size should be greater than or equals 100.' + ); + } + $this->optionChakraSize = intval($value); + return $this; + } + + /** + * Set chakra style. Chakra style provided should be 'north', 'south' or 'east'. + * + * @param string $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\UnexpectedValueException + */ + public function setOptionChakraStyle($value) + { + if (!in_array($value, Chakra::$style)) { + throw new Exception\UnexpectedValueException( + "Invalid chakra style provided should be 'north', 'south' or 'east'." + ); + } + $this->optionChakraStyle = strtolower($value); + return $this; + } + + /** + * Set border offset. Border offset should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\OutOfRangeException + */ + public function setOptionOffsetBorder($value) + { + if (!is_numeric($value) || intval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Border offset should be greater than or equals 0.' + ); + } + $this->optionOffsetBorder = intval($value); + return $this; + } + + /** + * Set width offset. Width offset should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\OutOfRangeException + */ + public function setOptionWidthOffsetLabel($value) + { + if (!is_numeric($value) || intval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Width offset should be greater than or equals 0.' + ); + } + $this->optionWidthOffsetLabel = intval($value); + return $this; + } + + /** + * Set height offset. Height offset should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\OutOfRangeException + */ + public function setOptionHeightOffsetLabel($value) + { + if (!is_numeric($value) || intval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Height offset should be greater than or equals 0.' + ); + } + $this->optionHeightOffsetLabel = intval($value); + return $this; + } + + /** + * Set graha label type. Label type provided should be 0, 1 or 2. + * + * @param int $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\UnexpectedValueException + */ + public function setOptionLabelGrahaType($value) + { + if (!in_array($value, [0, 1, 2])) { + throw new Exception\UnexpectedValueException( + "Invalid label type provided should be 0, 1 or 2." + ); + } + $this->optionLabelGrahaType = $value; + return $this; + } + + /** + * Set callable function. + * + * @param callable $value + * @return \Jyotish\Draw\Plot\Chakra\Renderer + * @throws Exception\RuntimeException + */ + public function setOptionLabelGrahaCallback($value) + { + if (!is_callable($value)) { + throw new Exception\RuntimeException("Function $value not supported."); + } + $this->optionLabelGrahaCallback = $value; + return $this; + } +} \ No newline at end of file diff --git a/src/Plot/Chakra/Style/AbstractChakra.php b/src/Plot/Chakra/Style/AbstractChakra.php new file mode 100644 index 0000000..f1f1929 --- /dev/null +++ b/src/Plot/Chakra/Style/AbstractChakra.php @@ -0,0 +1,121 @@ + + */ +abstract class AbstractChakra +{ + use \Jyotish\Base\Traits\DataTrait; + + /** + * North Indian style + */ + const STYLE_NORTH = 'north'; + /** + * South Indian style + */ + const STYLE_SOUTH = 'south'; + /** + * Eastern Indian Style + */ + const STYLE_EAST = 'east'; + + /** + * List of styles. + * + * @var array + */ + public static $style = [ + self::STYLE_NORTH, + self::STYLE_SOUTH, + self::STYLE_EAST, + ]; + + /** + * Analysis object. + * + * @var \Jyotish\Base\Analysis + */ + protected $Analysis = null; + + /** + * Chakra graha. + * + * @var string + */ + protected $chakraGraha; + + /** + * Chakra divider. + * + * @var int + */ + protected $chakraDivider; + + /** + * Coordinates of chakra bhavas. + * + * @var array + */ + protected $bhavaPoints = []; + + /** + * Constructor + * + * @param \Jyotish\Base\Data $Data + */ + public function __construct(\Jyotish\Base\Data $Data) + { + $this->setData($Data); + + $this->Analysis = new Analysis($Data); + } + + /** + * Get bhava points. + * + * @param int $size Size of chakra + * @param int $leftOffset Left offset + * @param int $topOffset Top offset + * @return array + */ + public function getBhavaPoints($size, $leftOffset = 0, $topOffset = 0) + { + $myPoints = []; + foreach ($this->bhavaPoints as $bhavaKey => $bhavaPoints) { + foreach ($bhavaPoints as $point => $value) { + if ($value != 0) { + if ($point % 2) { + $myPoints[$bhavaKey][] = $value * round($size / $this->chakraDivider) + $topOffset; + } else { + $myPoints[$bhavaKey][] = $value * round($size / $this->chakraDivider) + $leftOffset; + } + } else { + $myPoints[$bhavaKey][] = $point % 2 ? $topOffset : $leftOffset; + } + } + } + + return $myPoints; + } + + /** + * Get rashi label points. + */ + abstract public function getRashiLabelPoints(array $options); + + /** + * Get body label points. + */ + abstract public function getBodyLabelPoints(array $options); +} \ No newline at end of file diff --git a/src/Plot/Chakra/Style/East.php b/src/Plot/Chakra/Style/East.php new file mode 100644 index 0000000..62164a9 --- /dev/null +++ b/src/Plot/Chakra/Style/East.php @@ -0,0 +1,211 @@ + + */ +final class East extends AbstractChakra +{ + /** + * Chakra graha. + * + * @var string + */ + protected $chakraGraha = Graha::KEY_SY; + + /** + * Chakra divider. + * + * @var int + */ + protected $chakraDivider = 3; + + /** + * Coordinates of chakra bhavas. + * + * @var array + */ + protected $bhavaPoints = [ + 1 => [1, 0, 2, 0, 2, 1, 1, 1], + 2 => [0, 0, 1, 0, 1, 1], + 3 => [0, 0, 1, 1, 0, 1], + 4 => [0, 1, 1, 1, 1, 2, 0, 2], + 5 => [0, 3, 0, 2, 1, 2], + 6 => [0, 3, 1, 2, 1, 3], + 7 => [1, 2, 2, 2, 2, 3, 1, 3], + 8 => [2, 2, 3, 3, 2, 3], + 9 => [2, 2, 3, 2, 3, 3], + 10 => [2, 1, 3, 1, 3, 2, 2, 2], + 11 => [3, 0, 3, 1, 2, 1], + 12 => [2, 0, 3, 0, 2, 1], + ]; + + /** + * Get rashi label points. + * + * @param array $options + * @return array + */ + public function getRashiLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 3); + $offsetBorder = $options['offsetBorder']; + $offsetCorner3 = $offsetBorder * 3; + $offsetCorner4 = $offsetBorder * 4; + $rashis = $this->Analysis->getRashiInBhava($options['chakraVarga']); + + $myPoints = []; + foreach ($rashis as $rashi => $bhava) { + $bhava = $rashi; + + if ($bhava == 1) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][6] * $ratio + $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][7] * $ratio - $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 2) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][4] * $ratio - $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetCorner3; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 3) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetCorner4; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][3] * $ratio - $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 4) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][4] * $ratio - $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 5) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][4] * $ratio - $offsetCorner4; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][5] * $ratio + $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 6) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][3] * $ratio + $offsetCorner3; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 7) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][3] * $ratio + $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 8) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetCorner3; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 9) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetCorner4; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 10) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'top'; + } elseif ($bhava == 11) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][4] * $ratio + $offsetCorner4; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetBorder; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } else { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][4] * $ratio + $offsetBorder; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetCorner3; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } + } + return $myPoints; + } + + /** + * Get body label points. + * + * @param array $options + * @return array + */ + public function getBodyLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 3); + $offsetBorder = $options['offsetBorder']; + $offsetCorner = $offsetBorder * 4; + $offsetSum = []; + $bodies = $this->Analysis->getBodyInRashi($options['chakraVarga']); + + $myPoints = []; + foreach ($bodies as $graha => $bhava) { + if (!isset($offsetSum[$bhava])) $offsetSum[$bhava] = 0; + + if ($bhava == 1 || $bhava == 4 || $bhava == 7 || $bhava == 10) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][6] * $ratio + $offsetBorder + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][7] * $ratio - $ratio / 2; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 2) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetBorder; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 3) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } elseif ($bhava == 5) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $offsetCorner - $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } elseif ($bhava == 6) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $offsetBorder; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 8) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetCorner - $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][3] * $ratio - $offsetBorder; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 9) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][4] * $ratio - $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetCorner - $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } elseif ($bhava == 11) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio - $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } else { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetCorner - $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][3] * $ratio + $offsetBorder; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + } + return $myPoints; + } +} \ No newline at end of file diff --git a/src/Plot/Chakra/Style/North.php b/src/Plot/Chakra/Style/North.php new file mode 100644 index 0000000..ac30fb9 --- /dev/null +++ b/src/Plot/Chakra/Style/North.php @@ -0,0 +1,169 @@ + + */ +final class North extends AbstractChakra +{ + /** + * Chakra graha. + * + * @var string + */ + protected $chakraGraha = Graha::KEY_SK; + + /** + * Chakra divider. + * + * @var int + */ + protected $chakraDivider = 4; + + /** + * Coordinates of chakra bhavas. + * + * @var array + */ + protected $bhavaPoints = [ + 1 => [2, 2, 1, 1, 2, 0, 3, 1], + 2 => [1, 1, 0, 0, 2, 0], + 3 => [1, 1, 0, 2, 0, 0], + 4 => [2, 2, 1, 3, 0, 2, 1, 1], + 5 => [1, 3, 0, 4, 0, 2], + 6 => [1, 3, 2, 4, 0, 4], + 7 => [2, 2, 3, 3, 2, 4, 1, 3], + 8 => [3, 3, 4, 4, 2, 4], + 9 => [3, 3, 4, 2, 4, 4], + 10 => [2, 2, 3, 1, 4, 2, 3, 3], + 11 => [3, 1, 4, 0, 4, 2], + 12 => [3, 1, 2, 0, 4, 0,] + ]; + + /** + * Get rashi label points. + * + * @param array $options + * @return array + */ + public function getRashiLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 4); + $rashis = $this->Analysis->getRashiInBhava($options['chakraVarga']); + $offsetCorner = sqrt(2 * $options['offsetBorder'] * $options['offsetBorder']); + + $myPoints = []; + foreach ($rashis as $rashi => $bhava) { + if ($bhava == 1 || $bhava == 2 || $bhava == 12) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $offsetCorner; + $myPoints[$rashi]['textAlign'] = 'center'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 3 || $bhava == 4 || $bhava == 5) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio - $offsetCorner; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'middle'; + } elseif ($bhava == 6 || $bhava == 7 || $bhava == 8) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetCorner; + $myPoints[$rashi]['textAlign'] = 'center'; + $myPoints[$rashi]['textValign'] = 'top'; + } else { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetCorner; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'middle'; + } + } + return $myPoints; + } + + /** + * Get body label points. + * + * @param array $options + * @return array + */ + public function getBodyLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 4); + $offsetBorder = $options['offsetBorder']; + $offsetCorner = $offsetBorder * 5; + $offsetSum = []; + $bodies = $this->Analysis->getBodyInBhava($options['chakraVarga']); + + $myPoints = []; + foreach ($bodies as $graha => $bhava) { + $myPoints[$graha]['bhava'] = $bhava; + if (!isset($offsetSum[$bhava])) $offsetSum[$bhava] = 0; + + if ($bhava == 1) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $offsetCorner - $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'center'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } + if ($bhava == 2 || $bhava == 12) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][2] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][3] * $ratio + $offsetBorder; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + if ($bhava == 3 || $bhava == 5) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][4] * $ratio + $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][5] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } + if ($bhava == 4) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio - $offsetSum[$bhava] - $offsetCorner; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + if ($bhava == 6 || $bhava == 8) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][4] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][5] * $ratio - $offsetBorder; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'bottom'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + if ($bhava == 7) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'center'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } + if ($bhava == 9 || $bhava == 11) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][2] * $ratio - $offsetBorder; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][3] * $ratio + $offsetCorner + $offsetSum[$bhava]; + $myPoints[$graha]['textAlign'] = 'right'; + $myPoints[$graha]['textValign'] = 'top'; + $offsetSum[$bhava] += $options['heightOffsetLabel']; + } + if ($bhava == 10) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetSum[$bhava] + $offsetCorner; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + } + return $myPoints; + } +} \ No newline at end of file diff --git a/src/Plot/Chakra/Style/South.php b/src/Plot/Chakra/Style/South.php new file mode 100644 index 0000000..7579a2c --- /dev/null +++ b/src/Plot/Chakra/Style/South.php @@ -0,0 +1,137 @@ + + */ +final class South extends AbstractChakra +{ + /** + * Chakra graha. + * + * @var string + */ + protected $chakraGraha = Graha::KEY_GU; + + /** + * Chakra divider. + * + * @var int + */ + protected $chakraDivider = 4; + + /** + * Coordinates of chakra bhavas. + * + * @var array + */ + protected $bhavaPoints = [ + 1 => [2, 1, 1, 1, 1, 0, 2, 0], + 2 => [2, 1, 2, 0, 3, 0, 3, 1], + 3 => [3, 1, 3, 0, 4, 0, 4, 1], + 4 => [3, 2, 3, 1, 4, 1, 4, 2], + 5 => [3, 2, 4, 2, 4, 3, 3, 3], + 6 => [3, 3, 4, 3, 4, 4, 3, 4], + 7 => [2, 3, 3, 3, 3, 4, 2, 4], + 8 => [2, 3, 2, 4, 1, 4, 1, 3], + 9 => [1, 3, 1, 4, 0, 4, 0, 3], + 10 => [1, 2, 1, 3, 0, 3, 0, 2], + 11 => [1, 2, 0, 2, 0, 1, 1, 1], + 12 => [1, 1, 0, 1, 0, 0, 1, 0], + ]; + + /** + * Get rashi label points. + * + * @param array $options + * @return array + */ + public function getRashiLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 4); + $rashis = $this->Analysis->getRashiInBhava($options['chakraVarga']); + + $myPoints = []; + foreach ($rashis as $rashi) { + $bhava = $rashi; + + if ($bhava == 1 || $bhava == 11 || $bhava == 12) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio - $options['offsetBorder']; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $options['offsetBorder']; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 2 || $bhava == 3 || $bhava == 4) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $options['offsetBorder']; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $options['offsetBorder']; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'bottom'; + } elseif ($bhava == 5 || $bhava == 6 || $bhava == 7) { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $options['offsetBorder']; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $options['offsetBorder']; + $myPoints[$rashi]['textAlign'] = 'left'; + $myPoints[$rashi]['textValign'] = 'top'; + } else { + $myPoints[$rashi]['x'] = $this->bhavaPoints[$bhava][0] * $ratio - $options['offsetBorder']; + $myPoints[$rashi]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $options['offsetBorder']; + $myPoints[$rashi]['textAlign'] = 'right'; + $myPoints[$rashi]['textValign'] = 'top'; + } + } + return $myPoints; + } + + /** + * Get body label points. + * + * @param array $options + * @return array + */ + public function getBodyLabelPoints(array $options) + { + $ratio = round($options['chakraSize'] / 4); + $offsetBorder = $options['offsetBorder']; + $offsetSum = []; + $bodies = $this->Analysis->getBodyInRashi($options['chakraVarga']); + + $myPoints = []; + foreach ($bodies as $graha => $bhava) { + if (!isset($offsetSum[$bhava])) $offsetSum[$bhava] = 0; + + if ($bhava == 1 || $bhava == 11 || $bhava == 12) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][2] * $ratio + $offsetBorder + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][3] * $ratio - $ratio / 2; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 2 || $bhava == 3 || $bhava == 4) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio - $ratio / 2; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } elseif ($bhava == 5 || $bhava == 6 || $bhava == 7) { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][0] * $ratio + $offsetBorder + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][1] * $ratio + $ratio / 2; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } else { + $myPoints[$graha]['x'] = $this->bhavaPoints[$bhava][6] * $ratio + $offsetBorder + $offsetSum[$bhava]; + $myPoints[$graha]['y'] = $this->bhavaPoints[$bhava][7] * $ratio + $ratio / 2; + $myPoints[$graha]['textAlign'] = 'left'; + $myPoints[$graha]['textValign'] = 'middle'; + $offsetSum[$bhava] += $options['widthOffsetLabel']; + } + } + return $myPoints; + } +} \ No newline at end of file diff --git a/src/Renderer/AbstractRenderer.php b/src/Renderer/AbstractRenderer.php new file mode 100644 index 0000000..016e983 --- /dev/null +++ b/src/Renderer/AbstractRenderer.php @@ -0,0 +1,147 @@ + + */ +abstract class AbstractRenderer +{ + use \Jyotish\Base\Traits\OptionTrait; + use \Jyotish\Base\Traits\GetTrait; + + /** + * Renderer name. + * + * @var string + */ + protected $rendererName; + + /** + * Drawing Resource. + * + * @var mixed + */ + protected $Resource = null; + + protected $optionTopOffset = 0; + protected $optionLeftOffset = 0; + + protected $optionFontSize = 10; + protected $optionFontName = null; + protected $optionFontColor = '000'; + + protected $optionTextAlign = 'left'; + protected $optionTextValign = 'bottom'; + protected $optionTextOrientation = 0; + + protected $optionStrokeWidth = 1; + protected $optionStrokeColor = '000'; + + protected $optionFillColor = 'fff'; + + /** + * Set top offset. Top offset should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Renderer\AbstractRenderer + * @throws Exception\OutOfRangeException + */ + public function setOptionTopOffset($value) + { + if (!is_numeric($value) || intval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Top offset should be greater than or equals 0.' + ); + } + $this->optionTopOffset = intval($value); + return $this; + } + + /** + * Set left offset. Left offset should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Renderer\AbstractRenderer + * @throws Exception\OutOfRangeException + */ + public function setOptionLeftOffset($value) + { + if (!is_numeric($value) || intval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Left offset should be greater than or equals 0.' + ); + } + $this->optionLeftOffset = intval($value); + return $this; + } + + /** + * Set font size. Font size should be greater than or equals 8. + * + * @param int $value + * @return \Jyotish\Draw\Renderer\AbstractRenderer + * @throws Exception\OutOfRangeException + */ + public function setOptionFontSize($value) + { + if (!is_numeric($value) || intval($value) < 8) { + throw new Exception\OutOfRangeException( + 'Font size should be greater than or equals 8.' + ); + } + $this->optionFontSize = intval($value); + return $this; + } + + /** + * Set font color. + * + * @param string $value + * @return \Jyotish\Draw\Renderer\AbstractRenderer + */ + public function setOptionFontColor($value) + { + $this->optionFontColor = $value; + return $this; + } + + /** + * Set stroke width. Stroke width should be greater than or equals 0. + * + * @param int $value + * @return \Jyotish\Draw\Renderer\AbstractRenderer + * @throws Exception\OutOfRangeException + */ + public function setOptionStrokeWidth($value) + { + if (!is_numeric($value) || floatval($value) < 0) { + throw new Exception\OutOfRangeException( + 'Stroke width should be greater than or equals 0.' + ); + } + $this->optionStrokeWidth = $value; + return $this; + } + + /** + * Draw polygon. + */ + abstract public function drawPolygon(array $points, array $options = null); + + /** + * Draw text string. + */ + abstract public function drawText($text, $x, $y, array $options = null); + + /** + * Render the drawing. + */ + abstract public function render(); +} \ No newline at end of file diff --git a/src/Renderer/Exception/ExceptionInterface.php b/src/Renderer/Exception/ExceptionInterface.php new file mode 100644 index 0000000..63313b8 --- /dev/null +++ b/src/Renderer/Exception/ExceptionInterface.php @@ -0,0 +1,15 @@ + + */ +class Image extends AbstractRenderer +{ + /** + * Renderer name. + * + * @var string + */ + protected $rendererName = \Jyotish\Draw\Draw::RENDERER_IMAGE; + + /** + * Constructor + * + * @param int $width Width of drawing + * @param int $height Height of drawing + */ + public function __construct($width, $height) + { + $this->Resource = imagecreatetruecolor($width, $height); + + $color = $this->allocateColor($this->Resource, 255, 255, 255); + + imagefill($this->Resource, 0, 0, $color); + } + + /** + * Draw polygon. + * + * @param array $points An array containing the polygon's vertices. + * @param null|array $options + */ + public function drawPolygon(array $points, array $options = null) + { + $this->setOptions($options); + + $colorRgb = Utility::htmlToRgb($this->optionStrokeColor); + $color = $this->allocateColor($this->Resource, $colorRgb['r'], $colorRgb['g'], $colorRgb['b']); + + imagesetthickness($this->Resource, $this->optionStrokeWidth); + + $numPoints = count($points) / 2; + + imagepolygon($this->Resource, $points, $numPoints, $color); + } + + /** + * Draw text string. + * + * @param string $text Text for drawing + * @param int $x x-coordinate + * @param int $y y-coordinate + * @param array $options + * @throws Exception\RuntimeException + */ + public function drawText($text, $x = 0, $y = 0, array $options = null) + { + $this->setOptions($options); + + $colorRgb = Utility::htmlToRgb($this->optionFontColor); + $color = $this->allocateColor($this->Resource, $colorRgb['r'], $colorRgb['g'], $colorRgb['b']); + + if ($this->optionFontName == null) { + $this->optionFontName = 3; + } + + if (is_numeric($this->optionFontName)) { + if ($this->optionTextOrientation) { + throw new Exception\RuntimeException( + 'No orientation possible with GD internal font.' + ); + } + $fontWidth = imagefontwidth($this->optionFontName); + $fontHeight = imagefontheight($this->optionFontName); + + switch ($this->optionTextAlign) { + case 'left': + $positionX = $x; + break; + case 'center': + $positionX = $x - ceil(($fontWidth * strlen($text)) / 2); + break; + case 'right': + $positionX = $x - ($fontWidth * strlen($text)); + break; + } + + switch ($this->optionTextValign) { + case 'top': + $positionY = $y; + break; + case 'middle': + $positionY = $y - $fontHeight / 2; + break; + case 'bottom': + $positionY = $y - $fontHeight + 1; + break; + } + + imagestring( + $this->Resource, + $this->optionFontName, + $positionX, + $positionY, + $text, + $color + ); + } else { + if (!function_exists('imagettfbbox')) { + throw new Exception\RuntimeException( + 'A font was provided, but this instance of PHP does not have TTF (FreeType) support'); + } + + $box = imagettfbbox($this->optionFontSize, 0, $this->optionFontName, $text); + + switch ($this->optionTextAlign) { + case 'center': + $width = ($box[2] - $box[0]) / 2; + break; + case 'right': + $width = ($box[2] - $box[0]); + break; + case 'left': + default: + $width = 0; + break; + } + + switch ($this->optionTextValign) { + case 'top': + $height = ($box[1] - $box[7]); + break; + case 'middle': + $height = ($box[1] - $box[7]) / 2; + break; + case 'bottom': + default: + $height = 0; + break; + } + + imagettftext( + $this->Resource, + $this->optionFontSize, + $this->optionTextOrientation, + $x - ($width * cos(pi() * $this->optionTextOrientation / 180)) + ($height * sin(pi() * $this->optionTextOrientation / 180)), + $y + ($height * cos(pi() * $this->optionTextOrientation / 180)) + ($width * sin(pi() * $this->optionTextOrientation / 180)), + $color, + $this->optionFontName, + $text + ); + } + } + + /** + * Set font name. + * + * @param null|int|string $value + * @return \Jyotish\Draw\Renderer\Image + * @throws Exception\InvalidArgumentException + */ + public function setOptionFontName($value) + { + if (!is_null($value) && !is_int($value) && !is_string($value)) { + throw new Exception\InvalidArgumentException("Options 'fontName' should be null, integer or name of font."); + } else { + if (is_string($value) && !file_exists($value)) { + throw new Exception\InvalidArgumentException("The font '$value' does not exist."); + } + } + $this->optionFontName = $value; + return $this; + } + + /** + * Render the drawing. + */ + public function render() + { + header('Content-type: image/png'); + imagepng($this->Resource); + imagedestroy($this->Resource); + } + + private function allocateColor($image, $r, $g, $b, $alpha = 100) + { + $alphaValue = (127 / 100) * (100 - $alpha); + return(imagecolorallocatealpha($image, $r, $g, $b, $alphaValue)); + } +} \ No newline at end of file diff --git a/src/Renderer/Svg.php b/src/Renderer/Svg.php new file mode 100644 index 0000000..ad8f5b3 --- /dev/null +++ b/src/Renderer/Svg.php @@ -0,0 +1,182 @@ + + */ +class Svg extends AbstractRenderer +{ + /** + * Renderer name. + * + * @var string + */ + protected $rendererName = \Jyotish\Draw\Draw::RENDERER_SVG; + + /** + * SVG eletment. + * + * @var DOMElement + */ + protected $svg; + + protected $optionAttributes = []; + + /** + * Constructor + * + * @param int $width Width of drawing + * @param int $height Height of drawing + */ + public function __construct($width, $height) + { + $this->Resource = new DOMDocument('1.0', 'utf-8'); + $this->Resource->formatOutput = true; + + $this->svg = $this->Resource->createElement('svg'); + $this->svg->setAttribute('xmlns', "http://www.w3.org/2000/svg"); + $this->svg->setAttribute('version', '1.1'); + $this->svg->setAttribute('width', $width); + $this->svg->setAttribute('height', $height); + $this->svg->setAttribute('class', 'chakra'); + $this->svg->setAttribute('viewBox', "0 0 {$width} {$height}"); + + $this->Resource->appendChild($this->svg); + + $this->appendRootElement('style', ['type' => 'text/css'], ' + polygon:hover {fill: #eee;} + text {font-family: Arial;} + '); + $this->appendRootElement('rect', ['width' => $width, 'height' => $height, 'fill' => 'white']); + } + + /** + * Draw polygon. + * + * @param array $points An array containing the polygon's vertices. + * @param null|array $options + */ + public function drawPolygon(array $points, array $options = null) + { + $this->setOptions($options); + + $colorSrokeRgb = Utility::htmlToRgb($this->optionStrokeColor); + $colorStrokeString = 'rgb(' . implode(', ', $colorSrokeRgb) . ')'; + + $colorFillRgb = Utility::htmlToRgb($this->optionFillColor); + $colorFillString = 'rgb(' . implode(', ', $colorFillRgb) . ')'; + + $pointsString = implode(' ', $points); + + $attributes['points'] = $pointsString; + $attributes['fill'] = $colorFillString; + $attributes['stroke'] = $colorStrokeString; + $attributes['stroke-width'] = $this->optionStrokeWidth; + $attributes['stroke-linejoin'] = 'round'; + + if (isset($this->optionAttributes) && is_array($this->optionAttributes)) { + foreach ($this->optionAttributes as $name => $value) { + $attributes[$name] = $value; + } + } + + $this->appendRootElement('polygon', $attributes); + } + + /** + * Draw text string. + * + * @param string $text Text for drawing + * @param int $x x-coordinate + * @param int $y y-coordinate + * @param null|array $options + */ + public function drawText($text, $x = 0, $y = 0, array $options = null) + { + $this->setOptions($options); + + $colorRgb = Utility::htmlToRgb($this->optionFontColor); + $color = 'rgb(' . implode(', ', $colorRgb) . ')'; + + $attributes['x'] = $x; + $attributes['y'] = $y; + $attributes['fill'] = $color; + $attributes['font-size'] = $this->optionFontSize * 1.2; + + switch ($this->optionTextAlign) { + case 'center': + $textAnchor = 'middle'; + break; + case 'right': + $textAnchor = 'end'; + break; + case 'left': + default: + $textAnchor = 'start'; + break; + } + + switch ($this->optionTextValign) { + case 'top': + $attributes['y'] += $this->optionFontSize; + break; + case 'middle': + $attributes['y'] += $this->optionFontSize / 2; + break; + case 'bottom': + default: + $attributes['y'] += 0; + break; + } + + $attributes['style'] = 'text-anchor: ' . $textAnchor; + + $attributes['transform'] = 'rotate(' + . (- $this->optionTextOrientation) + . ', ' + . ($x) + . ', ' . ($y) + . ')'; + + $this->appendRootElement('text', $attributes, html_entity_decode($text, ENT_COMPAT | ENT_HTML5, 'UTF-8')); + } + + /** + * Render the drawing. + */ + public function render() + { + header("Content-Type: image/svg+xml"); + echo $this->Resource->saveXML(); + } + + private function appendRootElement($tagName, array $attributes = [], $textContent = null) + { + $newElement = $this->createElement($tagName, $attributes, $textContent); + $this->svg->appendChild($newElement); + } + + private function createElement($tagName, array $attributes = [], $textContent = null) + { + $element = $this->Resource->createElement($tagName); + foreach ($attributes as $k => $v) { + $element->setAttribute($k, $v); + } + if ($textContent !== null) { + $element->appendChild(new DOMText((string) $textContent)); + } + return $element; + } +} \ No newline at end of file