Extension:Go diagrams/godiag.php

, Morten Pahle 

* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * http://www.gnu.org/copyleft/gpl.html

* @author Stanislav Traykov  et al * @license GNU GPL * @version 0.8

* CHANGES: * 0.8  * 02 Apr 06 * XXX changed hash string for SGF/PNG XXX *                  * options l, r (float left/right) *                  * add $wgGodiagHintRegex *                  * 4 bugs fixed * 0.7  * 30 Mar 06 * better error reporting + bugfixes *                  * correct author in extension credits * 0.6  * 29 Mar 06 * generate to tmp and rename *                  * keep files in tmp if in a preview *                  * just one godiag dir with subdirs *                  * fix wipe (_) spacing *                  * $wgGodiagDefaultStyle * 0.5  * 04 Mar 06 * SGF output *		    * styles functionality *		    * board size and coordinate markers *		    * better PNG text output *		    * a few bugfixes * 0.3  * 02 Mar 06 * sensei's compat fixes * 0.2  * 02 Mar 06 * config in LocalSettings.php *		    * images with links can now also be cached *		    * semi-transparent link highlighting *		    * easily stylable HTML (and validated, too) *		    * cleaner code (drawing still ugly) */

// globals if(!isset($wgGodiagDirectory)) $wgGodiagDirectory = dirname($wgUploadDirectory). '/godiags'; if(!isset($wgGodiagPath)) $wgGodiagPath = "$wgScriptPath/godiags"; if(!isset($wgGodiagTTFont)) $wgGodiagTTFont = '/usr/share/fonts/bitstream-vera/Vera.ttf'; if(!isset($wgGodiagHintRegex)) $wgGodiagHintRegex='/(\d|10) (?:at|on) (\d)/'; if(!isset($wgGodiagDefaultStyle)) $wgGodiagDefaultStyle='normal'; if(!isset($wgGodiagSgfLinkTitle)) $wgGodiagSgfLinkTitle = 'download SGF'; if(!isset($wgGodiagSgfLinkText)) $wgGodiagSgfLinkText = ; // usually =  since an image is used if(!isset($wgGodiagSgfComment)) $wgGodiagSgfComment = "diagram from $wgSitename";

$wgGodiagDrawFunctab = array(',' => 'godiag_draw_hoshi',		'O' => 'godiag_draw_white',		'X' => 'godiag_draw_black',		'C' => 'godiag_draw_circle',		'S' => 'godiag_draw_square',		'B' => 'godiag_draw_black_circle',		'W' => 'godiag_draw_white_circle',		'#' => 'godiag_draw_black_square',		'@' => 'godiag_draw_white_square',		'_' => 'godiag_draw_wipe');

$wgGodiagSeqNo = 1; // if the same diagram is included more than once on a page, we need this to construct a unique image map id

$wgGodiagStyle = null;

// mediawiki reg $wgExtensionFunctions[] = 'godiag_extension_reg'; $wgExtensionCredits['parserhook'][] = array( 'name' => 'Go Diagrams', 'version' => '0.8', 'author' => 'Stanislav Traykov (SGF stuff adapted from A. Hollosi & M. Pahle)', 'url' => 'http://stan.gmuf.com/', );

function godiag_extension_reg { global $wgParser; $wgParser->setHook('go', 'godiag_get_html'); godiag_init_styles; }

// drawing functions /* * creates an anti-aliased circle */ function godiag_circ( &$img, $cx, $cy, $cr, $color) { // based on a comment by klaas at kosmokrator dot com // on http://php.net/manual/en/function.imageantialias.php (15-Feb-2006 05:22) $ir = $cr; $ix = 0; $iy = $ir; $ig = 2 * $ir - 3; $idgr = -6; $idgd = 4 * $ir - 10; $fill = imageColorExactAlpha( $img, $color[0], $color[1], $color[2], 0); imageLine( $img, $cx + $cr - 1, $cy, $cx, $cy, $fill ); imageLine( $img, $cx - $cr + 1, $cy, $cx - 1, $cy, $fill ); imageLine( $img, $cx, $cy + $cr - 1, $cx, $cy + 1, $fill ); imageLine( $img, $cx, $cy - $cr + 1, $cx, $cy - 1, $fill ); $draw = imageColorExactAlpha( $img, $color[0], $color[1], $color[2], 42); imageSetPixel( $img, $cx + $cr, $cy, $draw ); imageSetPixel( $img, $cx - $cr, $cy, $draw ); imageSetPixel( $img, $cx, $cy + $cr, $draw ); imageSetPixel( $img, $cx, $cy - $cr, $draw ); while ( $ix <= $iy - 2 ) { if ( $ig < 0 ) { $ig += $idgd; $idgd -= 8; $iy--; } else { $ig += $idgr; $idgd -= 4; }		$idgr -= 4; $ix++; imageLine( $img, $cx + $ix, $cy + $iy - 1, $cx + $ix, $cy + $ix, $fill ); imageLine( $img, $cx + $ix, $cy - $iy + 1, $cx + $ix, $cy - $ix, $fill ); imageLine( $img, $cx - $ix, $cy + $iy - 1, $cx - $ix, $cy + $ix, $fill ); imageLine( $img, $cx - $ix, $cy - $iy + 1, $cx - $ix, $cy - $ix, $fill ); imageLine( $img, $cx + $iy - 1, $cy + $ix, $cx + $ix, $cy + $ix, $fill ); imageLine( $img, $cx + $iy - 1, $cy - $ix, $cx + $ix, $cy - $ix, $fill ); imageLine( $img, $cx - $iy + 1, $cy + $ix, $cx - $ix, $cy + $ix, $fill ); imageLine( $img, $cx - $iy + 1, $cy - $ix, $cx - $ix, $cy - $ix, $fill ); $filled = 0; for ( $xx = $ix - 0.45; $xx < $ix + 0.5; $xx += 0.2 ) { for ( $yy = $iy - 0.45; $yy < $iy + 0.5; $yy += 0.2 ) { if ( sqrt( pow( $xx, 2 ) + pow( $yy, 2 ) ) < $cr ) $filled += 4; }		}		$draw = imageColorExactAlpha( $img, $color[0], $color[1], $color[2], ( 100 - $filled ) ); imageSetPixel( $img, $cx + $ix, $cy + $iy, $draw ); imageSetPixel( $img, $cx + $ix, $cy - $iy, $draw ); imageSetPixel( $img, $cx - $ix, $cy + $iy, $draw ); imageSetPixel( $img, $cx - $ix, $cy - $iy, $draw ); imageSetPixel( $img, $cx + $iy, $cy + $ix, $draw ); imageSetPixel( $img, $cx + $iy, $cy - $ix, $draw ); imageSetPixel( $img, $cx - $iy, $cy + $ix, $draw ); imageSetPixel( $img, $cx - $iy, $cy - $ix, $draw ); } } function godiag_draw_hoshi($im, $bx, $by) { global $wgGodiagStyle; $coords=godiag_get_coords($bx, $by); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['hoshi_radius'], $wgGodiagStyle['line_acolor']); } function godiag_draw_white($im, $bx, $by) { global $wgGodiagStyle; $coords=godiag_get_coords($bx, $by); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['stone_radius'], $wgGodiagStyle['white_rim_acolor']); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['stone_radius']-1, $wgGodiagStyle['white_acolor']); } function godiag_draw_black($im, $bx, $by) { global $wgGodiagStyle; $coords=godiag_get_coords($bx, $by); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['stone_radius'], $wgGodiagStyle['black_acolor']); } function godiag_draw_white_circle($im, $bx, $by) { global $wgGodiagStyle; $coords=godiag_get_coords($bx, $by); godiag_draw_white($im, $bx, $by); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['mark_radius'], $wgGodiagStyle['mark_acolor']); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['mark_radius']-2, $wgGodiagStyle['white_acolor']); } function godiag_draw_black_circle($im, $bx, $by) { global $wgGodiagStyle; $coords=godiag_get_coords($bx, $by); godiag_draw_black($im, $bx, $by); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['mark_radius'], $wgGodiagStyle['mark_acolor']); godiag_circ($im, $coords[0], $coords[1], $wgGodiagStyle['mark_radius']-2, $wgGodiagStyle['black_acolor']); } function godiag_draw_circle($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; $coords=godiag_get_coords($bx, $by); list($x, $y) = $coords=godiag_get_coords($bx, $by); $r=$wgGodiagStyle['mark_radius']; $sim = $wgGodiagStyle['circle_mark_img']['im']; $dim = $wgGodiagStyle['circle_mark_img']['dim']; imagecopymerge($im, $sim, $x-$r-1, $y-$r-1, 0, 0, $dim, $dim, 100); } function godiag_draw_square($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; list($x, $y) = $coords=godiag_get_coords($bx, $by); $x1=$x-($wgGodiagStyle['mark_sqheight']/2); $x2=$x+($wgGodiagStyle['mark_sqheight']/2); $y1=$y-($wgGodiagStyle['mark_sqheight']/2); $y2=$y+($wgGodiagStyle['mark_sqheight']/2); imagefilledrectangle($im, $x1, $y1, $x2, $y2, $wgGodiagDgm['mark_color']); } function godiag_draw_link($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; list($x, $y) = $coords=godiag_get_coords($bx, $by); $x1=$x-($wgGodiagStyle['link_sqheight']/2); $x2=$x+($wgGodiagStyle['link_sqheight']/2); $y1=$y-($wgGodiagStyle['link_sqheight']/2); $y2=$y+($wgGodiagStyle['link_sqheight']/2); imagefilledrectangle($im, $x1, $y1, $x2, $y2, $wgGodiagDgm['link_color']); } function godiag_draw_white_square($im, $bx, $by) { godiag_draw_white($im, $bx, $by); godiag_draw_square($im, $bx, $by); } function godiag_draw_black_square($im, $bx, $by) { godiag_draw_black($im, $bx, $by); godiag_draw_square($im, $bx, $by); } function godiag_draw_num($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; $str=$wgGodiagDgm['dia'][$by][$bx]; $str=($str=='0') ? '10' : $str; $blacks_turn = intval($str) % 2 == ($wgGodiagDgm['black_first'] ? 1 : 0); if($blacks_turn) godiag_draw_black($im, $bx, $by); else godiag_draw_white($im, $bx, $by); list($x, $y) = $coords=godiag_get_coords($bx, $by); $col=$blacks_turn ? $wgGodiagDgm['white_color'] : $wgGodiagDgm['black_color']; $box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], $str); $basex=$x - intval(abs($box[2]-$box[0])+1)/2 - $box[0]; imagettftext($im, $wgGodiagStyle['ttfont_sz'], 0, $basex, $y+$wgGodiagStyle['majusc_voffs'], $col, $wgGodiagStyle['ttfont'], $str); } function godiag_draw_let($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; list($x, $y) = $coords=godiag_get_coords($bx, $by); $str = $wgGodiagDgm['dia'][$by][$bx]; $box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], $str); $basex = $x - intval(abs($box[2]-$box[0])+1)/2 - $box[0]; $r = $wgGodiagStyle['stone_radius']-3; imagefilledrectangle($im, $x-$r, $y-$r, $x+$r, $y+$r, $wgGodiagDgm['goban_color']); imagettftext($im, $wgGodiagStyle['ttfont_sz'], 0, $basex, $y+$wgGodiagStyle['minusc_voffs'], $wgGodiagDgm['string_color'], $wgGodiagStyle['ttfont'], $str); } function godiag_draw_coord($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; list($x, $y) = $coords=godiag_get_coords($bx, $by); if($bx==-1) $str = $wgGodiagDgm['board_size'] - $wgGodiagDgm['offset_y'] - $by; else { $bx2 = $wgGodiagDgm['offset_x'] + $bx; $str=chr(65+$bx2+($bx2>7? 1 : 0));	}	$box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], $str); if($bx==-1) $basex=$x - $wgGodiagStyle['stone_radius']+($str<10?$wgGodiagStyle['sm_offs']:0); else { $basex=$x - intval(abs($box[2]-$box[0])+1)/2 - $box[0]; }	imagettftext($im, $wgGodiagStyle['ttfont_sz'], 0, $basex, $y+$wgGodiagStyle['majusc_voffs'], $wgGodiagDgm['string_color'], $wgGodiagStyle['ttfont'], $str); } function godiag_draw_wipe($im, $bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; list($x, $y) = $coords=godiag_get_coords($bx, $by); imagefilledrectangle($im, $x-$wgGodiagStyle['line_sp']/2+1, $y-$wgGodiagStyle['line_sp']/2+1, $x+$wgGodiagStyle['line_sp']/2, $y+$wgGodiagStyle['line_sp']/2, $wgGodiagDgm['goban_color']); } /* * draw and save diagram */ function godiag_save_diagram($dimh, $dimv, $filename_png) { global $wgGodiagStyle; global $wgGodiagDgm; global $wgGodiagDrawFunctab; $im = imagecreatetruecolor($dimh, $dimv); if(!$im) return(godiag_error_box("Cannot initialize GD image stream."));

//some things we only want to do once if(!array_key_exists('sm_offs', $wgGodiagStyle)) { // calc <10 number horiz offset for coords $box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], '1'); $box2 = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], '11'); $wgGodiagStyle['sm_offs'] = ($box2[2]-$box[1]) - ($box[2]-$box[0]) - 1; // vertical offsets for majuscules and minuscules $box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], 'a'); $wgGodiagStyle['minusc_voffs'] = (-$box[5])/2+1; $box = imagettfbbox($wgGodiagStyle['ttfont_sz'], 0, $wgGodiagStyle['ttfont'], 'A'); $wgGodiagStyle['majusc_voffs'] = (-$box[5])/2; }	if(!array_key_exists('circle_mark_img', $wgGodiagStyle)) { $r=$wgGodiagStyle['mark_radius']; $dim=$r*2+3; $xim = imagecreatetruecolor($dim, $dim); $gc = $wgGodiagStyle['goban_acolor']; $transp = imagecolorallocate($xim, $gc[0], $gc[1], $gc[2]); imagecolortransparent($xim, $transp); imagefill($xim, 0, 0, $transp); godiag_circ($xim, $r+1, $r+1, $r, $wgGodiagStyle['mark_acolor']); godiag_circ($xim, $r+1, $r+1, $r-2, $wgGodiagStyle['goban_acolor']); $wgGodiagStyle['circle_mark_img']['im'] = $xim; $wgGodiagStyle['circle_mark_img']['dim'] = $dim; }

$wgGodiagDgm['line_color'] = godiag_acolor2color($im, $wgGodiagStyle['line_acolor']); $wgGodiagDgm['mark_color'] = godiag_acolor2color($im, $wgGodiagStyle['mark_acolor']); $wgGodiagDgm['link_color'] = godiag_acolor2color($im, $wgGodiagStyle['link_acolor_alpha'], true); $wgGodiagDgm['goban_color'] = godiag_acolor2color($im, $wgGodiagStyle['goban_acolor']); $wgGodiagDgm['black_color'] = godiag_acolor2color($im, $wgGodiagStyle['black_acolor']); $wgGodiagDgm['white_color'] = godiag_acolor2color($im, $wgGodiagStyle['white_acolor']); $wgGodiagDgm['string_color'] = godiag_acolor2color($im, $wgGodiagStyle['string_acolor']);

imagefill($im, 0, 0, $wgGodiagDgm['goban_color']);

// draw lines $beginv=$wgGodiagDgm['edge_top'] ? $wgGodiagStyle['edge_sp'] : $wgGodiagStyle['line_begin']; $beginh=$wgGodiagDgm['edge_left'] ? $wgGodiagStyle['edge_sp'] : $wgGodiagStyle['line_begin']; $endv=$dimv - ($wgGodiagDgm['edge_bottom'] ? $wgGodiagStyle['edge_sp'] : $wgGodiagStyle['line_begin']) - 1; $endh=$dimh - ($wgGodiagDgm['edge_right'] ? $wgGodiagStyle['edge_sp'] : $wgGodiagStyle['line_begin']) - 1; if($wgGodiagDgm['coord_markers']) { $beginv += $wgGodiagStyle['coord_sp']; $beginh += $wgGodiagStyle['coord_sp']; }

// draw horizontal lines for($i=0; $i<$wgGodiagDgm['gridv']; $i+=1) { $coords=godiag_get_coords($i, $i); imageline($im, $beginh, $coords[0], $endh, $coords[0], $wgGodiagDgm['line_color']); }	// draw vertical lines for($i=0; $i<$wgGodiagDgm['gridh']; $i+=1) { $coords=godiag_get_coords($i, $i); imageline($im, $coords[0], $beginv, $coords[0], $endv, $wgGodiagDgm['line_color']); }

// draw coordinates (if requested) if($wgGodiagDgm['coord_markers']) { godiag_draw_coord($im, -1, 2); for($i=0; $i<$wgGodiagDgm['gridv']; $i++) godiag_draw_coord($im, -1, $i); for($i=0; $i<$wgGodiagDgm['gridh']; $i++) godiag_draw_coord($im, $i, -1); }

// draw rest foreach($wgGodiagDgm['dia'] as $by => $row) { foreach($row as $bx => $symb) { if($symb>='0' and $symb<='9') godiag_draw_num($im, $bx, $by); else if($symb>='a' and $symb<='z') godiag_draw_let($im, $bx, $by); else if($symb!='.') { if(!array_key_exists($symb, $wgGodiagDrawFunctab)) { imagedestroy($im); $oopshtml=htmlspecialchars("unknown symbol \"$symb\""); return godiag_error_box($oopshtml); }				$wgGodiagDrawFunctab[$symb]($im, $bx, $by); }			// draw link, if any if(array_key_exists($symb, $wgGodiagDgm['imappings'])) { godiag_draw_link($im, $bx, $by); }		}	}

// save if(!imagepng($im, $filename_png)) { imagedestroy($im); $hfilename=htmlspecialchars($filename_png); return godiag_error_box("Cannot output diagram to file."); }	imagedestroy($im); return 0; } // end of godiag_save_diagram

// other funcs function godiag_get_coords($bx, $by) { global $wgGodiagStyle; global $wgGodiagDgm; $additional_sp=0; if($wgGodiagDgm['coord_markers']) $additional_sp+=$wgGodiagStyle['coord_sp']; return array($bx*$wgGodiagStyle['line_sp']+$wgGodiagStyle['edge_sp']+$additional_sp,			$by*$wgGodiagStyle['line_sp']+$wgGodiagStyle['edge_sp']+$additional_sp); } function godiag_imap_area($bx, $by, $href) { global $wgGodiagStyle; list($x, $y) = $coords=godiag_get_coords($bx, $by); $x1=$x-$wgGodiagStyle['line_sp']/2; $y1=$y-$wgGodiagStyle['line_sp']/2; $x2=$x+$wgGodiagStyle['line_sp']/2; $y2=$y+$wgGodiagStyle['line_sp']/2; $title=htmlspecialchars($href); if(strpos($href, '://')) $href=$title; else $href=rawurlencode($href); return ""; } function godiag_acolor2color($im, $acolor, $alpha = false) { if($alpha) return imagecolorexactalpha($im, $acolor[0], $acolor[1], $acolor[2], $acolor[3]); else return imagecolorallocate($im, $acolor[0], $acolor[1], $acolor[2]); } function godiag_error_box($str) { return "Go diagram error: $str "; } function godiag_init_styles { global $wgGodiagStyles; global $wgGodiagTTFont; global $wgGodiagSgfLinkTitle, $wgGodiagSgfLinkText, $wgGodiagSgfComment; if(!isset($wgGodiagStyles) or !array_key_exists('normal', $wgGodiagStyles)) { $wgGodiagStyles['normal'] = array(			'board_max' => 40, // max size of go board			'sgf_comment' => $wgGodiagSgfComment,			'sgf_link_txt' => $wgGodiagSgfLinkText,			'sgf_link_title' => $wgGodiagSgfLinkTitle, // link title (tooltip)			'ttfont' => $wgGodiagTTFont, // true type font			'ttfont_sz' => 10, // font size in px (roughly half of line_sp)			'line_sp' => 22, // spacing between two lines			'edge_sp' => 14, // spacing on edge of board			'line_begin' => 4, // line begin (if not edge)			'coord_sp' => 20, // spacing for coordinates (2 * font_sz or more)			'stone_radius' => 11, // (line_sp / 2 works nice)			'mark_radius' => 5, // radius of circle mark (about half of stone radius)			'mark_sqheight' => 8, // height of square mark (somewhat less than stone radius)			'link_sqheight' => 21, // height of link highlight (line_sp - 1)			'hoshi_radius' => 3, // star point radius			'goban_acolor' => array (242, 180, 101), // background color in RGB 'black_acolor' => array(0, 0, 0), 'white_acolor' => array(255, 255, 255), 'white_rim_acolor' => array(70, 70, 70), 'mark_acolor' => array(244, 0, 0), 'link_acolor_alpha' => array(10, 50, 255, 96), // R G B alpha (=transparency) 'line_acolor' => array(0, 0, 0), 'string_acolor' => array(0, 0, 0));	}

if(!array_key_exists('large', $wgGodiagStyles)) { $wgGodiagStyles['large'] = array(			'board_max' => 40, // max size of go board			'sgf_comment' => $wgGodiagSgfComment,			'sgf_link_txt' => $wgGodiagSgfLinkText,			'sgf_link_title' => $wgGodiagSgfLinkTitle, // link title (tooltip)			'ttfont' => $wgGodiagTTFont, // true type font			'ttfont_sz' => 20,			'line_sp' => 44,			'edge_sp' => 28,			'line_begin' => 8,			'coord_sp' => 46,			'stone_radius' => 22,			'mark_radius' => 10,			'mark_sqheight' => 16,			'link_sqheight' => 43,			'hoshi_radius' => 5,			'goban_acolor' => array(242, 180, 101), // background color in RGB			'black_acolor' => array(0, 0, 0),			'white_acolor' => array(255, 255, 255),			'white_rim_acolor' => array(70, 70, 70),			'mark_acolor' => array(244, 0, 0),			'link_acolor_alpha' => array(10, 50, 255, 96), // R G B alpha (=transparency)			'line_acolor' => array(0, 0, 0),			'string_acolor' => array(0, 0, 0)); } } /* * calculates board size and offsets for SGF and coordinate drawing * returns an array (board_size, offset_x, offset_y) or false if there's a conflict */ function godiag_calc_offsets_and_size { global $wgGodiagDgm; $sizex = $wgGodiagDgm['gridh']; $sizey = $wgGodiagDgm['gridv']; $heightdefined = $wgGodiagDgm['edge_top'] && $wgGodiagDgm['edge_bottom']; $widthdefined = $wgGodiagDgm['edge_left'] && $wgGodiagDgm['edge_right']; $offset_x = 0; $offset_y = 0; if ($heightdefined) { if ($widthdefined && $sizex != $sizey) return false; if ($sizex > $sizey) return false; $size = $sizey; if ($wgGodiagDgm['edge_right'])	$offset_x = $size-$sizex; elseif (!$wgGodiagDgm['edge_left'])	$offset_x = ($size-$sizex)/2; }	elseif ($widthdefined) {		if ($sizey > $sizex) return false; $size = $sizex; if ($wgGodiagDgm['edge_bottom'])	$offset_y = $size-$sizey; elseif (!$wgGodiagDgm['edge_top'])	$offset_y = ($size-$sizey)/2; }	else {		$size = max($sizex, $sizey, $wgGodiagDgm['board_size']);

if ($wgGodiagDgm['edge_right'])	$offset_x = $size-$sizex; elseif (!$wgGodiagDgm['edge_left'])	$offset_x = ($size-$sizex)/2;

if ($wgGodiagDgm['edge_bottom'])	$offset_y = $size-$sizey; elseif (!$wgGodiagDgm['edge_top'])	$offset_y = ($size-$sizey)/2; }	return(array($size, intval($offset_x), intval($offset_y))); } // SGF function godiag_SGF { global $wgGodiagStyle; global $wgGodiagDgm; global $wgGodiagHintRegex; $rows = $wgGodiagDgm['dia']; $title = str_replace(']', '\]', $wgGodiagDgm['title']); $comment = str_replace(']', '\]', $wgGodiagStyle['sgf_comment']); $sizex = $wgGodiagDgm['gridh']; $sizey = $wgGodiagDgm['gridv']; $size = $wgGodiagDgm['board_size']; $offset_x = $wgGodiagDgm['offset_x']; $offset_y = $wgGodiagDgm['offset_y']; // SGF Root node string $firstcolor = $wgGodiagDgm['black_first'] ? 'B' : 'W'; $SGFString = "(GM[1]FF[4]SZ[$size]\n\n" .			"GN[$title]\n" .			"AP[GoDiag/MediaWiki]\n" .			"DT[".date("Y-m-d")."]\n" .			"PL[$firstcolor]\n" .			"C[$comment]\n";

$AB = array; $AW = array; $CR = array; $SQ = array; $LB = array;

if (!$wgGodiagDgm['black_first']) { $oddplayer = 'W'; $evenplayer = 'B'; } else { $oddplayer = 'B'; $evenplayer = 'W'; }

// output stones, numbers etc. for each row for ($ypos=0; $ypos<$sizey; $ypos++) { for ($xpos=0; $xpos<$sizex; $xpos++) { if(array_key_exists($ypos, $rows) && array_key_exists($xpos, $rows[$ypos])) $curchar = $rows[$ypos][$xpos]; else continue; $position = chr(97+$xpos+$offset_x). chr(97+$ypos+$offset_y);

if ($curchar == 'X' || $curchar == 'B' || $curchar == '#') $AB[] = $position;   // add black stone

if ($curchar == 'O' || $curchar == 'W' || $curchar == '@') $AW[] = $position;   // add white stone

if ($curchar == 'B' || $curchar == 'W' || $curchar == 'C') $CR[] = $position;   // add circle markup

if ($curchar == '#' || $curchar == '@' || $curchar == 'S') $SQ[] = $position;   // add circle markup

// other markup if ($curchar % 2 == 1)	// odd numbers (moves) {				$Moves[$curchar][1] = $position; $Moves[$curchar][2] = $oddplayer; }			elseif ($curchar*2 > 0 || $curchar == '0') // even num (moves) {				if ($curchar == '0') $curchar = '10'; $Moves[$curchar][1] = $position; $Moves[$curchar][2] = $evenplayer; }			elseif (($curchar >= 'a') && ($curchar <= 'z')) // letter markup $LB[] = "$position:$curchar"; } // for xpos loop }// for ypos loop

// parse title for hint of more moves if ($cnt = preg_match_all($wgGodiagHintRegex, $title, $match)) { for ($i=0; $i < $cnt; $i++) {			if (!isset($Moves[$match[1][$i]]) // only if not set on board			&&   isset($Moves[$match[2][$i]])) // referred move must be set {				$mvnum = $match[1][$i]; $Moves[$mvnum][1] = $Moves[$match[2][$i]][1]; $Moves[$mvnum][2] = $mvnum % 2 ? $oddplayer : $evenplayer; }		}	}

// build SGF string if (count($AB)) $SGFString .= 'AB['. join('][', $AB). "]\n"; if (count($AW)) $SGFString .= 'AW['. join('][', $AW). "]\n"; $Markup = ''; if (count($CR)) $Markup = 'CR['. join('][', $CR). "]\n"; if (count($SQ)) $Markup .= 'SQ['. join('][', $SQ). "]\n"; if (count($LB)) $Markup .= 'LB['. join('][', $LB). "]\n"; $SGFString .= "$Markup\n";

for ($mv=1; $mv <= 10; $mv++) {		if (isset($Moves[$mv])) { $SGFString .= ';'. $Moves[$mv][2]. '[' . $Moves[$mv][1]. ']';			$SGFString .= 'C['. $Moves[$mv][2]. $mv. "]\n"; $SGFString .= $Markup; }	}

#if (isset($Moves) && count($Moves) && $Markup != '') // if there are any moves, then repeat markup #  $SGFString .= ";$Markup"; $SGFString .= ")\n";

return $SGFString; } // end of godiag_SGF

/* * returns the HTML for inclusion in a wiki page and creates SGF and/or PNG on the fly, if needed */ function godiag_get_html($sourceandlinks) { global $wgRequest; global $wgGodiagDirectory, $wgGodiagPath; global $wgGodiagStyle; global $wgGodiagDefaultStyle; global $wgGodiagStyles; global $wgGodiagDgm; global $wgGodiagSeqNo;

// diagram specific things $wgGodiagDgm = array (		'dia' => array,		'edge_top' => false,		'edge_bottom' => false,		'edge_left' => false,		'edge_right' => false,		'black_first' => true,		'coord_markers' => false,		'offset_x' => 0,		'offset_y' => 0,		'board_size' => 19,		'gridh'	=> 0,		'gridv' => 0,		'imap_html' => '',		'imappings' => array,		'divclass' => 'right', // enclose HTML in div class="godiag-$whatever"		'break' => false);     // append br style="clear: left|right" to HTML

// this will be concatenated to so we can compute a hash $str_for_hash='';

// strip empty space and final newline $sourceandlinks=preg_replace('/(^|\n)\s*/',"$1", $sourceandlinks); $sourceandlinks=preg_replace("/\s*$/",'', $sourceandlinks);

// separate source from links part $source_parts=preg_split("/\n[\$\s]*(?=\[)/", $sourceandlinks, 2);

// links: store mappings for image map if(array_key_exists(1, $source_parts)) foreach (explode("\n", $source_parts[1]) as $link_line) { if(preg_match('/\[\s*([^\s])\s*\|\s*([^\]]+?)\s*\]/', $link_line, $matches)) { $wgGodiagDgm['imappings'][$matches[1]]=$matches[2]; }	}	ksort($wgGodiagDgm['imappings']); foreach($wgGodiagDgm['imappings'] as $symb => $href) { $str_for_hash .= $symb. '!' . $href. '!';	}

// source $source_lines=explode("\n", $source_parts[0]);

// header preg_match('/(\$\$[^\s]*)?\s*(.*)/', $source_lines[0], $matches); $hdr=$matches[1]; $heading=$matches[2]; $hdr=explode('#', $hdr); $h_ops = $hdr[0]; $style = array_key_exists(1, $hdr) ? $hdr[1] : $wgGodiagDefaultStyle; if(!array_key_exists($style, $wgGodiagStyles)) return godiag_error_box('no such style: ' . htmlspecialchars("\"$style\"")); $wgGodiagStyle=$wgGodiagStyles[$style]; if(strpos($h_ops, 'W')) { $wgGodiagDgm['black_first'] = false; }	if(strpos($h_ops, 'b')) { $wgGodiagDgm['break'] = 'true'; }	if(strpos($h_ops, 'c')) { $wgGodiagDgm['coord_markers'] = true; }	if(strpos($h_ops, 'l')) { $wgGodiagDgm['divclass'] = 'left'; }	if(strpos($h_ops, 'r')) { $wgGodiagDgm['divclass'] = 'right'; }	if(preg_match('/(\d+)/', $h_ops, $matches)) { $wgGodiagDgm['board_size'] = $matches[1]; }	$wgGodiagDgm['title'] = $heading; $heading = htmlspecialchars($heading); $last=count($source_lines)-1; if(preg_match('/^(\$|\s)*[-+]+\s*$/', $source_lines[$last])) { $wgGodiagDgm['edge_bottom']=true; unset($source_lines[$last]); }	unset($source_lines[0]); if(preg_match('/^(\$|\s)*[-+]+\s*$/', $source_lines[1])) { $wgGodiagDgm['edge_top']=true; unset($source_lines[1]); }

// get the diagram into $wgGodiagDgm['dia'][y][x], figure out dimensions and edges, // and generate html for image map $row=0; foreach ($source_lines as $source_line) { if(preg_match('/^(\s|\$)*\|/', $source_line)) { $wgGodiagDgm['edge_left']=true; } if(preg_match('/\|\s*$/', $source_line)) { $wgGodiagDgm['edge_right']=true; } $plainstr=str_replace(array('$', ' ', '|'), '', $source_line); $str_for_hash .= '$'. $plainstr; $as_array=preg_split('//', $plainstr, -1, PREG_SPLIT_NO_EMPTY); $len=count($as_array); foreach($as_array as $bx => $symb) { if(array_key_exists($symb, $wgGodiagDgm['imappings'])) { $wgGodiagDgm['imap_html'] .= godiag_imap_area($bx, $row, $wgGodiagDgm['imappings'][$symb]); }		}		if($len>$wgGodiagDgm['gridh']) { $wgGodiagDgm['gridh']=$len; } $wgGodiagDgm['dia'][$row++]=$as_array; }	$wgGodiagDgm['gridv']=$row;

// calc dimensions $dimh=($wgGodiagDgm['gridh']-1)*$wgGodiagStyle['line_sp']+$wgGodiagStyle['edge_sp']*2+1; $dimv=($wgGodiagDgm['gridv']-1)*$wgGodiagStyle['line_sp']+$wgGodiagStyle['edge_sp']*2+1; if($wgGodiagDgm['coord_markers']) { $dimh += $wgGodiagStyle['coord_sp']; $dimv += $wgGodiagStyle['coord_sp']; }	if(($offs_sz_arr=godiag_calc_offsets_and_size)===false) { return godiag_error_box('non-square board'); }	list($wgGodiagDgm['board_size'], //this will be overwritten if it conflicts with other input		$wgGodiagDgm['offset_x'],		$wgGodiagDgm['offset_y'])  =   $offs_sz_arr;

if($wgGodiagDgm['board_size'] > $wgGodiagStyle['board_max']) return godiag_error_box('board too large (max is '. $wgGodiagStyle['board_max']. ')');

//determine implicit edges if($wgGodiagDgm['gridv'] >= $wgGodiagDgm['board_size'] - 1) { $wgGodiagDgm['edge_top'] = true; if($wgGodiagDgm['gridv'] == $wgGodiagDgm['board_size']) $wgGodiagDgm['edge_bottom']=true; }	if($wgGodiagDgm['gridh'] >= $wgGodiagDgm['board_size'] - 1) { $wgGodiagDgm['edge_left']=true; if($wgGodiagDgm['gridh'] == $wgGodiagDgm['board_size']) $wgGodiagDgm['edge_right']=true; }

// compute hashes $str_for_hash .= '!'. $wgGodiagDgm['black_first'] . '!' . $wgGodiagDgm['edge_top'] . '!' . $wgGodiagDgm['edge_bottom'] . '!' . $wgGodiagDgm['edge_left'] . '!' . $wgGodiagDgm['edge_right'];

$str_for_hash_sgf = $str_for_hash . '!' . $wgGodiagDgm['board_size'] . '!' . $wgGodiagDgm['title'];

$str_for_hash_img = $str_for_hash . '!' . ($wgGodiagDgm['coord_markers'] ? $wgGodiagDgm['board_size'] : false);

$md5hash_png=md5($str_for_hash_img . '!' . $style); $md5hash_sgf=md5($str_for_hash_sgf); $filename_png="$md5hash_png.png"; $filename_sgf="$md5hash_sgf.sgf";

// create proper image map and attribute for IMG tag $map_id = 'god'. $wgGodiagSeqNo++. $md5hash_png; if($wgGodiagDgm['imap_html']) { $wgGodiagDgm['imap_html'] = "". $wgGodiagDgm['imap_html']. ' ';		$godiag_imap_imgs = "usemap=\"#$map_id\""; } else $godiag_imap_imgs = '';

// determine dirs, create some if necessary $tmp_directory = "$wgGodiagDirectory/tmp"; $tmp_path = "$wgGodiagPath/tmp"; $png_directory = "$wgGodiagDirectory/png"; $png_path = "$wgGodiagPath/png"; $sgf_directory = "$wgGodiagDirectory/sgf"; $sgf_path = "$wgGodiagPath/sgf";

foreach(array($tmp_directory, $png_directory, $sgf_directory) as $dir) if(!is_dir($dir) and !mkdir($dir, 0755)) return godiag_error_box('Cannot create ' . basename($dir) . ' directory.'); elseif (!is_writable($dir)) return godiag_error_box('Directory ' . basename($dir) . ' not writable.');

// are we called from a preview? $wiki_preview = $wgRequest->getCheck('wpPreview') || $wgRequest->getCheck('wpLivePreview');

// does the PNG and/or SGF file exist already? $png_exists = file_exists($png_directory . '/' . $filename_png); $sgf_exists = file_exists($sgf_directory . '/' . $filename_sgf);

// now we have the HTML to be returned if everything went OK $sgf_href = $sgf_path. '/' . $filename_sgf; $png_href = $png_path. '/' . $filename_png; if($wiki_preview) { if(!$sgf_exists) { $sgf_href = $tmp_path. '/' . $filename_sgf; } if(!$png_exists) { $png_href = $tmp_path. '/' . $filename_png; } }	$titletxt = $wgGodiagStyle['sgf_link_title']; $return_str = '' . $wgGodiagDgm['imap_html'] . ''			. ' '				. " ". $wgGodiagStyle['sgf_link_txt']. ' ' . $heading. '  ';	if($wgGodiagDgm['break']) { $return_str .= ''; }	// if we don't have the PNG for this diagram, create it	if(!$png_exists) { $draw_result = godiag_save_diagram($dimh, $dimv, $tmp_directory . '/' . $filename_png); if($draw_result!==0) { return $draw_result; // contains error message }		if(!$wiki_preview) { $ok=rename($tmp_directory . '/' . $filename_png,				$png_directory . '/' . $filename_png); if(!$ok) return godiag_error_box("Cannot move diagram to PNG directory."); }	}	// if we don't have the SGF for this diagram, create it	if(!$sgf_exists) { // md5hash_sgf.png $ok=false; if($sgff = fopen($tmp_directory. '/' . $filename_sgf, 'w')) { $sgf_data = godiag_SGF; $sgf_len = strlen($sgf_data); $n_written = fwrite($sgff, $sgf_data); if (!fclose($sgff) or $n_written != $sgf_len) return godiag_error_box("Error writing to SGF file."); if(!$wiki_preview) $ok=rename($tmp_directory . '/' . $filename_sgf,					$sgf_directory . '/' . $filename_sgf); else $ok=true; }		if(!$ok) return godiag_error_box("Cannot create SGF file."); }	return $return_str; } // end of godiag_get_html ?>