. */ namespace Fisharebest\Webtrees\Controller; use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Theme; /** * Controller for the fan chart */ class FanchartController extends ChartController { /** @var int Style of fanchart */ public $fan_style; /** @var int Width of fanchart (a percentage) */ public $fan_width; /** @var int Number of generations to display */ public $generations; /** * Create the controller */ public function __construct() { global $WT_TREE; parent::__construct(); $default_generations = $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'); // Extract the request parameters $this->fan_style = Filter::getInteger('fan_style', 2, 4, 3); $this->fan_width = Filter::getInteger('fan_width', 50, 500, 100); $this->generations = Filter::getInteger('generations', 2, 9, $default_generations); if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: http://en.wikipedia.org/wiki/Family_tree#Fan_chart - %s is an individual’s name */ I18N::translate('Fan chart of %s', $this->root->getFullName()) ); } else { $this->setPageTitle(I18N::translate('Fan chart')); } } /** * A list of options for the chart style. * * @return string[] */ public function getFanStyles() { return array( 2 => /* I18N: layout option for the fan chart */ I18N::translate('half circle'), 3 => /* I18N: layout option for the fan chart */ I18N::translate('three-quarter circle'), 4 => /* I18N: layout option for the fan chart */ I18N::translate('full circle'), ); } /** * split and center text by lines * * @param string $data input string * @param int $maxlen max length of each line * * @return string $text output string */ public function splitAlignText($data, $maxlen) { $RTLOrd = array(215, 216, 217, 218, 219); $lines = explode("\n", $data); // more than 1 line : recursive calls if (count($lines) > 1) { $text = ''; foreach ($lines as $line) { $text .= $this->splitAlignText($line, $maxlen) . "\n"; } return $text; } // process current line word by word $split = explode(' ', $data); $text = ''; $line = ''; // do not split hebrew line $found = false; foreach ($RTLOrd as $ord) { if (strpos($data, chr($ord)) !== false) { $found = true; } } if ($found) { $line = $data; } else { foreach ($split as $word) { $len = strlen($line); $wlen = strlen($word); if (($len + $wlen) < $maxlen) { if (!empty($line)) { $line .= ' '; } $line .= "$word"; } else { $p = max(0, (int) (($maxlen - $len) / 2)); if (!empty($line)) { $line = str_repeat(' ', $p) . $line; // center alignment using spaces $text .= $line . "\n"; } $line = $word; } } } // last line if (!empty($line)) { $len = strlen($line); if (in_array(ord($line{0}), $RTLOrd)) { $len /= 2; } $p = max(0, (int) (($maxlen - $len) / 2)); $line = str_repeat(' ', $p) . $line; // center alignment using spaces $text .= $line; } return $text; } /** * Generate both the HTML and PNG components of the fan chart * * The HTML and PNG components both require the same co-ordinate calculations, * so we generate them using the same code, but we send them in separate * HTTP requests. * * @param string $what "png" or "html" * * @return string */ public function generateFanChart($what) { $treeid = $this->sosaAncestors($this->generations); $fanw = 640 * $this->fan_width / 100; $fandeg = 90 * $this->fan_style; $html = ''; $treesize = count($treeid) + 1; // generations count $gen = log($treesize) / log(2) - 1; $sosa = $treesize - 1; // fan size if ($fandeg == 0) { $fandeg = 360; } $fandeg = min($fandeg, 360); $fandeg = max($fandeg, 90); $cx = $fanw / 2 - 1; // center x $cy = $cx; // center y $rx = $fanw - 1; $rw = $fanw / ($gen + 1); $fanh = $fanw; // fan height if ($fandeg == 180) { $fanh = round($fanh * ($gen + 1) / ($gen * 2)); } if ($fandeg == 270) { $fanh = round($fanh * 0.86); } $scale = $fanw / 640; // image init $image = imagecreate($fanw, $fanh); $white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF); imagefilledrectangle($image, 0, 0, $fanw, $fanh, $white); imagecolortransparent($image, $white); $color = imagecolorallocate( $image, hexdec(substr(Theme::theme()->parameter('chart-font-color'), 0, 2)), hexdec(substr(Theme::theme()->parameter('chart-font-color'), 2, 2)), hexdec(substr(Theme::theme()->parameter('chart-font-color'), 4, 2))); $bgcolor = imagecolorallocate( $image, hexdec(substr(Theme::theme()->parameter('chart-background-u'), 0, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-u'), 2, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-u'), 4, 2)) ); $bgcolorM = imagecolorallocate( $image, hexdec(substr(Theme::theme()->parameter('chart-background-m'), 0, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-m'), 2, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-m'), 4, 2)) ); $bgcolorF = imagecolorallocate( $image, hexdec(substr(Theme::theme()->parameter('chart-background-f'), 0, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-f'), 2, 2)), hexdec(substr(Theme::theme()->parameter('chart-background-f'), 4, 2)) ); // imagemap $imagemap = ''; switch ($what) { case 'html': return $html . $imagemap . '