Extension:Go diagrams/godiag.php

From MediaWiki.org
Jump to: navigation, search
<?php
/*
 * godiag.php - Go diagrams extension for MediaWiki 1.6/1.5
 *
 * Copyright 2006 Stanislav Traykov
 *
 * SGF output stuff and input syntax adapted from
 * Sensei's Library (http://senseis.xmp.net)
 * http://senseis.xmp.net/files/sltxt2png.php.txt published under GPL:
 *
 * Copyright (C) 2001-2004 by
 * Arno Hollosi <ahollosi@xmp.net>, Morten Pahle <morten@pahle.org.uk>
 
 * 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 <st at gmuf.com> 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 ( hypot( $xx, $yy ) < $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 "<area href=\"$href\" title=\"$title\" alt=\"$title\" coords=\"$x1,$y1,$x2,$y2\"/>";
}
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 "<strong style=\"border: 2px solid red;\">Go diagram error: $str</strong>";
}
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'] = "<map id=\"$map_id\" name=\"$map_id\">" . $wgGodiagDgm['imap_html'] . '</map>';
                $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 = '<div class="godiag-' . $wgGodiagDgm['divclass'] . '">'
                        . $wgGodiagDgm['imap_html']
                        . '<div class="godiagi" style="width: ' . $dimh . 'px;"><img class="godiagimg" src=" ' . $png_href . '" alt="go diagram" '
                                . $godiag_imap_imgs . '/>'
                        . '<div class="godiagheading">'
                                . "<div class=\"godiagsgf\"> <a href=\"$sgf_href\" title=\"$titletxt\">" . $wgGodiagStyle['sgf_link_txt'] . '</a></div>'
                                . $heading . '</div></div></div>';
        if($wgGodiagDgm['break']) {
                $return_str .= '<br class="godiag-' . $wgGodiagDgm['divclass'] . '"/>';
        }
 
        // 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()
Personal tools
Namespaces

Variants
Actions
Navigation
Support
Download
Development
Communication
Print/export
Toolbox