. */ /** * For more information, please contact */ namespace Goat1000\SVGGraph; /** * Class for defining marker graphics as symbols */ class Markers { private $graph; public function __construct(&$graph) { $this->graph =& $graph; } /** * Creates contents of a marker */ private function createContent($type, $size, $fill, $stroke_width, $stroke_colour, $opacity, $angle, $more = null) { $marker = ['fill' => $fill]; if(!empty($stroke_colour) && $stroke_colour != 'none') { $marker['stroke'] = $stroke_colour; if(!empty($stroke_width)) $marker['stroke-width'] = $stroke_width; } if($opacity > 0.0 && $opacity < 1.0) $marker['opacity'] = $opacity; // check for custom or image markers $content = null; if($type[0] == '<') { $content = $type; $type = 'custom'; } elseif(strncmp($type, 'image:', 6) == 0) { $svg_text = new Text($this->graph); $image_path = $svg_text->substr($type, 6); $type = 'image'; } elseif(strncmp($type, 'figure:', 7) == 0) { $svg_text = new Text($this->graph); $figure = $svg_text->substr($type, 7); $type = 'figure'; } $a = $size; // will be repeated a lot, and 'a' is smaller $element = 'path'; switch($type) { case 'triangle' : $o = $a * tan(M_PI / 6); $h = $a / cos(M_PI / 6); $marker['d'] = new PathData('M', $a, $o, 'L', 0, -$h, 'L', -$a, $o, 'z'); break; case 'diamond' : $marker['d'] = new PathData('M', 0, -$a, 'L', $a, 0, 0, $a, -$a, 0, 'z'); break; case 'square' : $element = 'rect'; $marker['x'] = $marker['y'] = -$a; $marker['width'] = $marker['height'] = $a * 2; break; case 'x' : $xform = new Transform; $xform->rotate(45); $marker['transform'] = $xform; // no break - 'x' is a cross rotated by 45 degrees case 'cross' : $t = $a / 4; $marker['d'] = new PathData('M', -$a, -$t); $marker['d']->add('L', -$a, $t, -$t, $t, -$t, $a); $marker['d']->add($t, $a, $t, $t, $a, $t); $marker['d']->add($a, -$t, $t, -$t, $t, -$a); $marker['d']->add(-$t, -$a, -$t, -$t, 'z'); break; case 'octagon' : $t = $a * sin(M_PI / 8); $marker['d'] = new PathData('M', $t, -$a); $marker['d']->add('L', $a, -$t, $a, $t, $t, $a, -$t, $a); $marker['d']->add(-$a, $t, -$a, -$t, -$t, -$a, 'z'); break; case 'star' : $t = $a * 0.382; $x1 = $t * sin(M_PI * 0.8); $y1 = $t * cos(M_PI * 0.8); $x2 = $a * sin(M_PI * 0.6); $y2 = $a * cos(M_PI * 0.6); $x3 = $t * sin(M_PI * 0.4); $y3 = $t * cos(M_PI * 0.4); $x4 = $a * sin(M_PI * 0.2); $y4 = $a * cos(M_PI * 0.2); $marker['d'] = new PathData('M', 0, -$a); $marker['d']->add('L', $x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4, 0, $t); $marker['d']->add(-$x4, $y4, -$x3, $y3, -$x2, $y2, -$x1, $y1, 'z'); break; case 'threestar' : $t = $a / 4; $t1 = $t * cos(M_PI / 6); $t2 = $t * sin(M_PI / 6); $a1 = $a * cos(M_PI / 6); $a2 = $a * sin(M_PI / 6); $marker['d'] = new PathData('M', 0, -$a); $marker['d']->add('L', $t1, -$t2, $a1, $a2, 0, $t); $marker['d']->add(-$a1, $a2, -$t1, -$t2, 'z'); break; case 'fourstar' : $t = $a / 4; $marker['d'] = new PathData('M', 0, -$a, 'L', $t, -$t, $a, 0, $t, $t); $marker['d']->add(0, $a, -$t, $t, -$a, 0, -$t, -$t, 'z'); break; case 'eightstar' : $t = $a * sin(M_PI / 8); $marker['d'] = new PathData('M', 0, -$t); $marker['d']->add('L', $t, -$a, $t, -$t, $a, -$t, $t, 0); $marker['d']->add($a, $t, $t, $t, $t, $a, 0, $t); $marker['d']->add(-$t, $a, -$t, $t, -$a, $t, -$t, 0); $marker['d']->add(-$a, -$t, -$t, -$t, -$t, -$a, 'z'); break; case 'asterisk' : $t = $a / 3; $x1 = $a * sin(M_PI * 0.9); $y1 = $a * cos(M_PI * 0.9); $x2 = $t * sin(M_PI * 0.8); $y2 = $t * cos(M_PI * 0.8); $x3 = $a * sin(M_PI * 0.7); $y3 = $a * cos(M_PI * 0.7); $x4 = $a * sin(M_PI * 0.5); $y4 = $a * cos(M_PI * 0.5); $x5 = $t * sin(M_PI * 0.4); $y5 = $t * cos(M_PI * 0.4); $x6 = $a * sin(M_PI * 0.3); $y6 = $a * cos(M_PI * 0.3); $x7 = $a * sin(M_PI * 0.1); $y7 = $a * cos(M_PI * 0.1); $marker['d'] = new PathData('M', $x1, $y1); $marker['d']->add('L', $x2, $y2, $x3, $y3, $x4, $y4, $x5, $y5); $marker['d']->add($x6, $y6, $x7, $y7, 0, $t, -$x7, $y7); $marker['d']->add(-$x6, $y6, -$x5, $y5, -$x4, $y4, -$x3, $y3); $marker['d']->add(-$x2, $y2, -$x1, $y1, 'z'); break; case 'pentagon' : $x1 = $a * sin(M_PI * 0.4); $y1 = $a * cos(M_PI * 0.4); $x2 = $a * sin(M_PI * 0.2); $y2 = $a * cos(M_PI * 0.2); $marker['d'] = new PathData('M', 0, -$a); $marker['d']->add('L', $x1, -$y1, $x2, $y2, -$x2, $y2, -$x1, -$y1, 'z'); break; case 'hexagon' : $x = $a * sin(M_PI / 3); $y = $a * cos(M_PI / 3); $marker['d'] = new PathData('M', 0, -$a); $marker['d']->add('L', $x, -$y, $x, $y, 0, $a, -$x, $y, -$x, -$y, 'z'); break; case 'image' : $element = 'image'; $marker['xlink:href'] = $image_path; $marker['x'] = $marker['y'] = -$size; $marker['width'] = $size * 2; $marker['height'] = $size * 2; break; case 'custom' : $element = 'g'; break; case 'figure' : $element = 'g'; $figure_id = $this->graph->figures->getFigure($figure); if(empty($figure_id)) throw new \Exception('Figure [' . $figure . '] not defined'); return [$figure_id]; break; case 'circle' : default : $element = 'circle'; $marker['r'] = $size; } // angle happens here because the shape might already have a transform if($angle != 0) { $xform = new Transform; $xform->rotate($angle); if(isset($marker['transform'])) $marker['transform']->add($xform); else $marker['transform'] = $xform; } // extra attributes added at end if(is_array($more)) $marker = array_merge($marker, $more); return $this->graph->element($element, $marker, null, $content); } /** * Creates a marker, returning its ID */ public function create($type, $size, $fill, $stroke_width, $stroke_colour, $opacity, $angle, $more = null) { $marker_content = $this->createContent($type, $size, $fill, $stroke_width, $stroke_colour, $opacity, $angle, $more); // if the return is an array, it contains the ID if(is_array($marker_content)) return $marker_content[0]; return $this->graph->defs->defineSymbol($marker_content); } /** * Creates a SVG marker element, returning its ID */ public function createSVGMarker($type, $size, $fill, $stroke_width, $stroke_colour, $opacity, $angle, $more = null) { $marker_content = $this->createContent($type, $size, $fill, $stroke_width, $stroke_colour, $opacity, $angle, $more); $sz = new Number($size); $sz2 = new Number($size * 2); $marker = [ 'viewBox' => "-{$sz} -{$sz} {$sz2} {$sz2}", 'markerWidth' => $sz, 'markerHeight' => $sz, 'refX' => $sz, 'orient' => 'auto', ]; return $this->graph->defs->addElement('marker', $marker, $marker_content); } }