. */ /** * For more information, please contact */ namespace Goat1000\SVGGraph; class CrossHairs { private $graph; private $show_h = false; private $show_v = false; private $left, $top, $width, $height; private $x_axis, $y_axis; private $assoc; private $flip_axes; private $encoding; public function __construct(&$graph, $left, $top, $w, $h, $x_axis, $y_axis, $assoc, $flip_axes, $encoding) { if($graph->getOption('crosshairs')) { $this->show_h = $graph->getOption('crosshairs_show_h'); $this->show_v = $graph->getOption('crosshairs_show_v'); } if(!$this->show_h && !$this->show_v) return; $this->left = $left; $this->top = $top; $this->width = $w; $this->height = $h; $this->x_axis = $x_axis; $this->y_axis = $y_axis; $this->assoc = $assoc; $this->flip_axes = $flip_axes; $this->encoding = $encoding; $this->graph =& $graph; } /** * Returns TRUE if the crosshairs are enabled */ public function enabled() { return $this->show_h || $this->show_v; } /** * Returns a horizontal or vertical hair */ private function getHair($orientation, $ch) { // line is always added, stays hidden if not enabled if($orientation == 'h') $hch = ['class' => 'chX', 'x2' => $ch['x1'] + $this->width]; else $hch = ['class' => 'chY', 'y2' => $ch['y1'] + $this->height]; $show = 'show_' . $orientation; if($this->$show) { $stroke = $this->graph->getOption('crosshairs_colour_' . $orientation, 'crosshairs_colour'); $hch['stroke'] = new Colour($this->graph, $stroke, false, false); $hch['stroke-width'] = $this->graph->getOption( 'crosshairs_stroke_width_' . $orientation, 'crosshairs_stroke_width'); $opacity = $this->graph->getOption( 'crosshairs_opacity_' . $orientation, 'crosshairs_opacity'); if($opacity > 0 && $opacity < 1) $hch['opacity'] = $opacity; $dash = $this->graph->getOption('crosshairs_dash_' . $orientation, 'crosshairs_dash'); if(!empty($dash)) $hch['stroke-dasharray'] = $dash; } return $this->graph->element('line', array_merge($ch, $hch)); } /** * Returns the crosshair code and also adds the JS and defs */ public function getCrossHairs() { if(!($this->show_v || $this->show_h)) return ''; // make the crosshair lines $crosshairs = ''; $ch = [ 'x1' => $this->left, 'y1' => $this->top, 'x2' => $this->left, 'y2' => $this->top, 'visibility' => 'hidden', // don't show them to start with! ]; $crosshairs .= $this->getHair('h', $ch); $crosshairs .= $this->getHair('v', $ch); $text_options = [ 'back_colour', 'round', 'stroke_width', 'colour', 'font_size', 'font', 'font_weight', 'padding', 'space', ]; $t_opt = []; foreach($text_options as $opt) $t_opt[$opt] = $this->graph->getOption('crosshairs_text_' . $opt); // text group for grid details $text_group = ['id' => $this->graph->newId(), 'visibility' => 'hidden']; $text_rect = [ 'x' => '0', 'y' => '0', 'width' => '10', 'height' => '10', 'fill' => new Colour($this->graph, $t_opt['back_colour']), ]; if($t_opt['round']) $text_rect['rx'] = $text_rect['ry'] = $t_opt['round']; if($t_opt['stroke_width']) { $text_rect['stroke-width'] = $t_opt['stroke_width']; $text_rect['stroke'] = $t_opt['colour']; } $font_size = max(3, Number::units($t_opt['font_size'])); $text_element = [ 'x' => 0, 'y' => $font_size, 'font-family' => $t_opt['font'], 'font-size' => $font_size, 'fill' => new Colour($this->graph, $t_opt['colour']), ]; $weight = $t_opt['font_weight']; if($weight && $weight != 'normal') $text_element['font-weight'] = $weight; $svg_text = new Text($this->graph); $text = $this->graph->element('g', $text_group, null, $this->graph->element('rect', $text_rect) . $svg_text->text('', $font_size, $text_element)); $this->graph->addBackMatter($text); // add in the details of the grid scales $zero_x = $this->x_axis->zero(); $scale_x = $this->x_axis->unit(); $zero_y = $this->y_axis->zero(); $scale_y = $this->y_axis->unit(); $prec_x = $this->graph->getOption('crosshairs_text_precision_h', max(0, ceil(log10($scale_x)))); $prec_y = $this->graph->getOption('crosshairs_text_precision_v', max(0, ceil(log10($scale_y)))); $scale_x = new Number($scale_x); $scale_x->precision = 7; $scale_y = new Number($scale_y); $scale_y->precision = 7; $gridx_attrs = [ 'function' => 'strValueX', 'zero' => $zero_x, 'scale' => $scale_x, 'precision' => $prec_x, ]; $gridy_attrs = [ 'function' => 'strValueY', 'zero' => $zero_y, 'scale' => $scale_y, 'precision' => $prec_y, ]; $chtextitem_attrs = [ 'type' => 'xy', 'groupid' => $text_group['id'], ]; $u = $this->x_axis->afterUnits(); if(!empty($u)) $chtextitem_attrs['unitsx'] = $u; $u = $this->y_axis->afterUnits(); if(!empty($u)) $chtextitem_attrs['unitsy'] = $u; $u = $this->x_axis->beforeUnits(); if(!empty($u)) $chtextitem_attrs['unitsbx'] = $u; $u = $this->y_axis->beforeUnits(); if(!empty($u)) $chtextitem_attrs['unitsby'] = $u; $log_x = $this->graph->getOption(['log_axis_x', 0]); $log_y = $this->graph->getOption(['log_axis_y', 0]); if($log_x || $log_y) { $base_y = $this->graph->getOption('log_axis_y_base'); $base_x = $this->graph->getOption('log_axis_x_base'); $log_h = $this->flip_axes ? $log_y : $log_x; $log_v = $this->flip_axes ? $log_x : $log_y; if($log_h) { $gridx_attrs['base'] = $this->flip_axes ? $base_y : $base_x; $gridx_attrs['zero'] = $this->x_axis->value(0); $gridx_attrs['scale'] = $this->x_axis->value($this->width); $this->graph->getJavascript()->addFunction('logStrValueX'); $gridx_attrs['function'] = 'logStrValueX'; } if($log_v) { $gridy_attrs['base'] = $this->flip_axes ? $base_x : $base_y; $gridy_attrs['zero'] = $this->y_axis->value(0); $gridy_attrs['scale'] = $this->y_axis->value($this->height); $this->graph->getJavascript()->addFunction('logStrValueY'); $gridy_attrs['function'] = 'logStrValueY'; } } if($this->graph->getOption('datetime_keys') && (method_exists($this->x_axis, 'GetFormat') || method_exists($this->y_axis, 'GetFormat'))) { $dtf = new DateTimeFormatter; if($this->flip_axes) { $this->graph->getJavascript()->addFunction('dateStrValueY'); $zy = (int)$this->y_axis->value(0); $ey = (int)$this->y_axis->value($this->width); $dt = new \DateTime('@' . $zy); $gridy_attrs['scale'] = ($ey - $zy) / $this->height; $gridy_attrs['zero'] = $dtf->format($dt, 'c', true); $gridy_attrs['function'] = 'dateStrValueY'; $gridy_attrs['format'] = $this->y_axis->getFormat(); } else { $this->graph->getJavascript()->addFunction('dateStrValueX'); $zx = (int)$this->x_axis->value(0); $ex = (int)$this->x_axis->value($this->width); $dt = new \DateTime('@' . $zx); $gridx_attrs['scale'] = ($ex - $zx) / $this->width; $gridx_attrs['zero'] = $dtf->format($dt, 'c', true); $gridx_attrs['function'] = 'dateStrValueX'; $gridx_attrs['format'] = $this->x_axis->getFormat(); } $long_days = $dtf->getLongDays(); $short_days = $dtf->getShortDays(); $long_months = $dtf->getLongMonths(); $short_months = $dtf->getShortMonths(); foreach($long_days as $day) $this->graph->getJavascript()->insertVariable('daysLong', null, $day); foreach($short_days as $day) $this->graph->getJavascript()->insertVariable('daysShort', null, $day); foreach($long_months as $month) $this->graph->getJavascript()->insertVariable('monthsLong', null, $month); foreach($short_months as $month) $this->graph->getJavascript()->insertVariable('monthsShort', null, $month); } // build associative data keys XML $keys_xml = ''; if($this->assoc) { $k_max = $this->graph->getMaxKey(); for($i = 0; $i <= $k_max; ++$i) { $k = $this->graph->getKey($i); $keys_xml .= $this->graph->element('svggraph:key', ['value' => $k]); } $keys_xml = $this->graph->element('svggraph:keys', null, null, $keys_xml); // choose a rounding function $round_function = 'kround'; if($this->graph->getOption('label_centre')) $round_function = 'kroundDown'; $this->graph->getJavascript()->addFunction($round_function); // set the string function if($this->flip_axes) { $this->graph->getJavascript()->addFunction('keyStrValueY'); $gridy_attrs['function'] = 'keyStrValueY'; $gridy_attrs['round'] = $round_function; } else { $this->graph->getJavascript()->addFunction('keyStrValueX'); $gridx_attrs['function'] = 'keyStrValueX'; $gridx_attrs['round'] = $round_function; } } $gridx = $this->graph->element('svggraph:gridx', $gridx_attrs); $gridy = $this->graph->element('svggraph:gridy', $gridy_attrs); $chtext = $this->graph->element('svggraph:chtext', null, null, $this->graph->element('svggraph:chtextitem', $chtextitem_attrs)); $xml = $gridx . $gridy . $chtext . $keys_xml; $defs = $this->graph->element('svggraph:data', ['xmlns:svggraph' => 'http://www.goat1000.com/svggraph'], null, $xml); $this->graph->defs->add($defs); // add the main function at the end - it can fill in any defaults $this->graph->getJavascript()->addFunction('crosshairs'); return $crosshairs; } }