1) $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . substr($sToken, 0, -1); $cPhraseQuote = null; } else { // If not, add the token to the phrase, with a single leading space if necessary $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . $sToken; } } else { // Will the current token start a phrase? if (strpos($sQuotes, $sToken[0]) !== false) { // Will the current token end the phrase? if ((strlen($sToken) > 1) && ($sToken[0] === substr($sToken, -1, 1))) { // The current token begins AND ends the phrase, trim the quotes $sPhrase = substr($sToken, 1, -1); } else { // Remove the leading quote $sPhrase = substr($sToken, 1); $cPhraseQuote = $sToken[0]; } } else $sPhrase = $sToken; } } // If, at this point, we are not within a phrase, the prepared phrase is complete and can be added to the array if (($cPhraseQuote === null) && ($sPhrase != null)) { $output[] = $sPhrase; $sPhrase = null; } } while ($sToken !== false); // Stop when we receive FALSE from strtok() return $output; } // wrapper around imagecolorallocate to try and re-use palette slots where possible function myimagecolorallocate($image, $red, $green, $blue) { // it's possible that we're being called early - just return straight away, in that case if(!isset($image)) return(-1); $existing=imagecolorexact($image, $red, $green, $blue); if ($existing > -1) return $existing; return (imagecolorallocate($image, $red, $green, $blue)); } function screenshotify($input) { $tmp = $input; $tmp = preg_replace("/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/","127.0.0.1",$tmp); $tmp = preg_replace("/([A-Za-z]{3,})/e","str_repeat('x',strlen('\\1'))",$tmp); return($tmp); } function render_colour($col) { if (($col[0] == -1) && ($col[1] == -1) && ($col[1] == -1)) { return 'none'; } else if (($col[0] == -2) && ($col[1] == -2) && ($col[1] == -2)) { return 'copy'; } else if (($col[0] == -3) && ($col[1] == -3) && ($col[1] == -3)) { return 'contrast'; } else { return sprintf("%d %d %d", $col[0], $col[1], $col[2]); } } // take the same set of points that imagepolygon does, but don't close the shape function imagepolyline($image, $points, $npoints, $color) { for ($i=0; $i < ($npoints - 1); $i++) { imageline($image, $points[$i * 2], $points[$i * 2 + 1], $points[$i * 2 + 2], $points[$i * 2 + 3], $color); } } // draw a filled round-cornered rectangle function imagefilledroundedrectangle($image , $x1 , $y1 , $x2 , $y2 , $radius, $color) { imagefilledrectangle($image, $x1,$y1+$radius, $x2,$y2-$radius, $color); imagefilledrectangle($image, $x1+$radius,$y1, $x2-$radius,$y2, $color); imagefilledarc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); imagefilledarc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); imagefilledarc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); imagefilledarc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); # bool imagefilledarc ( resource $image , int $cx , int $cy , int $width , int $height , int $start , int $end , int $color , int $style ) } // draw a round-cornered rectangle function imageroundedrectangle( $image , $x1 , $y1 , $x2 , $y2 , $radius, $color ) { imageline($image, $x1+$radius, $y1, $x2-$radius, $y1, $color); imageline($image, $x1+$radius, $y2, $x2-$radius, $y2, $color); imageline($image, $x1, $y1+$radius, $x1, $y2-$radius, $color); imageline($image, $x2, $y1+$radius, $x2, $y2-$radius, $color); imagearc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 180, 270, $color); imagearc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 270, 360, $color); imagearc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 90, 180, $color); imagearc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 90, $color); } function imagecreatefromfile($filename) { $bgimage=NULL; $formats = imagetypes(); if (is_readable($filename)) { list($width, $height, $type, $attr) = getimagesize($filename); switch($type) { case IMAGETYPE_GIF: if(imagetypes() & IMG_GIF) { $bgimage=imagecreatefromgif($filename); } else { warn("Image file $filename is GIF, but GIF is not supported by your GD library. [WMIMG01]\n"); } break; case IMAGETYPE_JPEG: if(imagetypes() & IMG_JPEG) { $bgimage=imagecreatefromjpeg($filename); } else { warn("Image file $filename is JPEG, but JPEG is not supported by your GD library. [WMIMG02]\n"); } break; case IMAGETYPE_PNG: if(imagetypes() & IMG_PNG) { $bgimage=imagecreatefrompng($filename); } else { warn("Image file $filename is PNG, but PNG is not supported by your GD library. [WMIMG03]\n"); } break; default: warn("Image file $filename wasn't recognised (type=$type). Check format is supported by your GD library. [WMIMG04]\n"); break; } } else { warn("Image file $filename is unreadable. Check permissions. [WMIMG05]\n"); } return $bgimage; } // taken from here: // http://www.php.net/manual/en/function.imagefilter.php#62395 // ( with some bugfixes and changes) // // Much nicer colorization than imagefilter does, AND no special requirements. // Preserves white, black and transparency. // function imagecolorize($im, $r, $g, $b) { //We will create a monochromatic palette based on //the input color //which will go from black to white //Input color luminosity: this is equivalent to the //position of the input color in the monochromatic //palette $lum_inp = round(255 * ($r + $g + $b) / 765); //765=255*3 //We fill the palette entry with the input color at its //corresponding position $pal[$lum_inp]['r'] = $r; $pal[$lum_inp]['g'] = $g; $pal[$lum_inp]['b'] = $b; //Now we complete the palette, first we'll do it to //the black,and then to the white. //FROM input to black //=================== //how many colors between black and input $steps_to_black = $lum_inp; //The step size for each component if ($steps_to_black) { $step_size_red = $r / $steps_to_black; $step_size_green = $g / $steps_to_black; $step_size_blue = $b / $steps_to_black; } for ($i = $steps_to_black; $i >= 0; $i--) { $pal[$steps_to_black - $i]['r'] = $r - round($step_size_red * $i); $pal[$steps_to_black - $i]['g'] = $g - round($step_size_green * $i); $pal[$steps_to_black - $i]['b'] = $b - round($step_size_blue * $i); } //From input to white: //=================== //how many colors between input and white $steps_to_white = 255 - $lum_inp; if ($steps_to_white) { $step_size_red = (255 - $r) / $steps_to_white; $step_size_green = (255 - $g) / $steps_to_white; $step_size_blue = (255 - $b) / $steps_to_white; } else $step_size_red = $step_size_green = $step_size_blue = 0; //The step size for each component for ($i = ($lum_inp + 1); $i <= 255; $i++) { $pal[$i]['r'] = $r + round($step_size_red * ($i - $lum_inp)); $pal[$i]['g'] = $g + round($step_size_green * ($i - $lum_inp)); $pal[$i]['b'] = $b + round($step_size_blue * ($i - $lum_inp)); } //--- End of palette creation //Now,let's change the original palette into the one we //created for ($c = 0; $c < imagecolorstotal($im); $c++) { $col = imagecolorsforindex($im, $c); $lum_src = round(255 * ($col['red'] + $col['green'] + $col['blue']) / 765); $col_out = $pal[$lum_src]; # printf("%d (%d,%d,%d) -> %d -> (%d,%d,%d)\n", $c, # $col['red'], $col['green'], $col['blue'], # $lum_src, # $col_out['r'], $col_out['g'], $col_out['b'] # ); imagecolorset($im, $c, $col_out['r'], $col_out['g'], $col_out['b']); } return($im); } // find the point where a line from x1,y1 through x2,y2 crosses another line through x3,y3 and x4,y4 // (the point might not be between those points, but beyond them) // - doesn't handle parallel lines. In our case we will never get them. // - make sure we remove colinear points, or this will not be true! function line_crossing($x1,$y1,$x2,$y2, $x3,$y3,$x4,$y4) { // First, check that the slope isn't infinite. // if it is, tweak it to be merely huge if($x1 != $x2) { $slope1 = ($y2-$y1)/($x2-$x1); } else { $slope1 = 1e10; debug("Slope1 is infinite.\n");} if($x3 != $x4) { $slope2 = ($y4-$y3)/($x4-$x3); } else { $slope2 = 1e10; debug("Slope2 is infinite.\n");} $a1 = $slope1; $a2 = $slope2; $b1 = -1; $b2 = -1; $c1 = ($y1 - $slope1 * $x1 ); $c2 = ($y3 - $slope2 * $x3 ); $det_inv = 1/($a1*$b2 - $a2*$b1); $xi = (($b1*$c2 - $b2*$c1)*$det_inv); $yi = (($a2*$c1 - $a1*$c2)*$det_inv); return(array($xi,$yi)); } // rotate a list of points around cx,cy by an angle in radians, IN PLACE function RotateAboutPoint(&$points, $cx,$cy, $angle=0) { $npoints = count($points)/2; for($i=0;$i<$npoints;$i++) { $ox = $points[$i*2] - $cx; $oy = $points[$i*2+1] - $cy; $rx = $ox * cos($angle) - $oy*sin($angle); $ry = $oy * cos($angle) + $ox*sin($angle); $points[$i*2] = $rx + $cx; $points[$i*2+1] = $ry + $cy; } } // calculate the points for a span of the curve. We pass in the distance so far, and the array index, so that // the chunk of array generated by this function can be array_merged with existing points from before. // Considering how many array functions there are, PHP has horrible list support // Each point is a 3-tuple - x,y,distance - which is used later to figure out where the 25%, 50% marks are on the curve function calculate_catmull_rom_span($startn, $startdistance, $numsteps, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) { $Ap_x=-$x0 + 3 * $x1 - 3 * $x2 + $x3; $Bp_x=2 * $x0 - 5 * $x1 + 4 * $x2 - $x3; $Cp_x=-$x0 + $x2; $Dp_x=2 * $x1; $Ap_y=-$y0 + 3 * $y1 - 3 * $y2 + $y3; $Bp_y=2 * $y0 - 5 * $y1 + 4 * $y2 - $y3; $Cp_y=-$y0 + $y2; $Dp_y=2 * $y1; $d=2; $n=$startn; $distance=$startdistance; $lx=$x0; $ly=$y0; $allpoints[]=array ( $x0, $y0, $distance ); for ($i=0; $i <= $numsteps; $i++) { $t=$i / $numsteps; $t2=$t * $t; $t3=$t2 * $t; $x=(($Ap_x * $t3) + ($Bp_x * $t2) + ($Cp_x * $t) + $Dp_x) / $d; $y=(($Ap_y * $t3) + ($Bp_y * $t2) + ($Cp_y * $t) + $Dp_y) / $d; if ($i > 0) { $step=sqrt((($x - $lx) * ($x - $lx)) + (($y - $ly) * ($y - $ly))); $distance=$distance + $step; $allpoints[$n]=array ( $x, $y, $distance ); $n++; } $lx=$x; $ly=$y; } return array($allpoints, $distance, $n); } function find_distance_coords(&$pointarray,$distance) { // We find the nearest lower point for each distance, // then linearly interpolate to get a more accurate point // this saves having quite so many points-per-curve $index=find_distance($pointarray, $distance); $ratio=($distance - $pointarray[$index][2]) / ($pointarray[$index + 1][2] - $pointarray[$index][2]); $x = $pointarray[$index][0] + $ratio * ($pointarray[$index + 1][0] - $pointarray[$index][0]); $y = $pointarray[$index][1] + $ratio * ($pointarray[$index + 1][1] - $pointarray[$index][1]); return(array($x,$y,$index)); } function find_distance_coords_angle(&$pointarray,$distance) { // This is the point we need list($x,$y,$index) = find_distance_coords($pointarray,$distance); // now to find one either side of it, to get a line to find the angle of $left = $index; $right = $left+1; $max = count($pointarray)-1; // if we're right up against the last point, then step backwards one if($right>=$max) { $left--; $right--; } # if($left<=0) { $left = 0; } $x1 = $pointarray[$left][0]; $y1 = $pointarray[$left][1]; $x2 = $pointarray[$right][0]; $y2 = $pointarray[$right][1]; $dx = $x2 - $x1; $dy = $y2 - $y1; $angle = rad2deg(atan2(-$dy,$dx)); return(array($x,$y,$index,$angle)); } // return the index of the point either at (unlikely) or just before the target distance // we will linearly interpolate afterwards to get a true point - pointarray is an array of 3-tuples produced by the function above function find_distance(&$pointarray, $distance) { $left=0; $right=count($pointarray) - 1; if ($left == $right) return ($left); // if the distance is zero, there's no need to search (and it doesn't work anyway) if($distance==0) return($left); // if it's a point past the end of the line, then just return the end of the line // Weathermap should *never* ask for this, anyway if ($pointarray[$right][2] < $distance) { return ($right); } // if somehow we have a 0-length curve, then don't try and search, just give up // in a somewhat predictable manner if ($pointarray[$left][2] == $pointarray[$right][2]) { return ($left); } while ($left <= $right) { $mid=floor(($left + $right) / 2); if (($pointarray[$mid][2] < $distance) && ($pointarray[$mid + 1][2] >= $distance)) { return $mid; } if ($distance <= $pointarray[$mid][2]) { $right=$mid - 1; } else { $left=$mid + 1; } } print "FELL THROUGH\n"; die ("Howie's crappy binary search is wrong after all.\n"); } // Give a list of key points, calculate a curve through them // return value is an array of triples (x,y,distance) function calc_curve(&$in_xarray, &$in_yarray,$pointsperspan = 32) { // search through the point list, for consecutive duplicate points // (most common case will be a straight link with both NODEs at the same place, I think) // strip those out, because they'll break the binary search/centre-point stuff $last_x=NULL; $last_y=NULL; for ($i=0; $i < count($in_xarray); $i++) { if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug ("Dumping useless duplicate point on curve\n"); } else { $xarray[]=$in_xarray[$i]; $yarray[]=$in_yarray[$i]; } $last_x=$in_xarray[$i]; $last_y=$in_yarray[$i]; } // only proceed if we still have at least two points! if(count($xarray) <= 1) { warn ("Arrow not drawn, as it's 1-dimensional.\n"); return (array(NULL, NULL, NULL, NULL)); } // duplicate the first and last points, so that all points are drawn // (C-R normally would draw from x[1] to x[n-1] array_unshift($xarray, $xarray[0]); array_unshift($yarray, $yarray[0]); $x=array_pop($xarray); $y=array_pop($yarray); array_push($xarray, $x); array_push($xarray, $x); array_push($yarray, $y); array_push($yarray, $y); $npoints=count($xarray); $curvepoints=array ( ); // add in the very first point manually (the calc function skips this one to avoid duplicates, which mess up the distance stuff) $curvepoints[]=array ( $xarray[0], $yarray[0], 0 ); $np=0; $distance=0; for ($i=0; $i < ($npoints - 3); $i++) { list($newpoints, $distance, $np)=calculate_catmull_rom_span($np, $distance, $pointsperspan, $xarray[$i], $yarray[$i], $xarray[$i + 1], $yarray[$i + 1], $xarray[$i + 2], $yarray[$i + 2], $xarray[$i + 3], $yarray[$i + 3]); $curvepoints=$curvepoints + $newpoints; } return ($curvepoints); } // Give a list of key points, calculate a "curve" through them // return value is an array of triples (x,y,distance) // this is here to mirror the real 'curve' version when we're using angled VIAs // it means that all the stuff that expects an array of points with distances won't be upset. function calc_straight(&$in_xarray, &$in_yarray,$pointsperspan = 12) { // search through the point list, for consecutive duplicate points // (most common case will be a straight link with both NODEs at the same place, I think) // strip those out, because they'll break the binary search/centre-point stuff $last_x=NULL; $last_y=NULL; for ($i=0; $i < count($in_xarray); $i++) { if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug ("Dumping useless duplicate point on curve\n"); } else { $xarray[]=$in_xarray[$i]; $yarray[]=$in_yarray[$i]; } $last_x=$in_xarray[$i]; $last_y=$in_yarray[$i]; } // only proceed if we still have at least two points! if(count($xarray) <= 1) { warn ("Arrow not drawn, as it's 1-dimensional.\n"); return (array(NULL, NULL, NULL, NULL)); } $npoints=count($xarray); $curvepoints=array(); $np=0; $distance=0; for ($i=0; $i < ($npoints -1); $i++) { // still subdivide the straight line, becuase other stuff makes assumptions about // how often there is a point - at least find_distance_coords_angle breaks $newdistance = sqrt( pow($xarray[$i+1] - $xarray[$i],2) + pow($yarray[$i+1] - $yarray[$i],2) ); $dx = ($xarray[$i+1] - $xarray[$i])/$pointsperspan; $dy = ($yarray[$i+1] - $yarray[$i])/$pointsperspan; $dd = $newdistance/$pointsperspan; for($j=0; $j< $pointsperspan; $j++) { $x = $xarray[$i]+$j*$dx; $y = $yarray[$i]+$j*$dy; $d = $distance + $j*$dd; $curvepoints[] = array($x,$y,$d); $np++; } $distance += $newdistance; } $curvepoints[] = array($xarray[$npoints-1],$yarray[$npoints-1],$distance); # print_r($curvepoints); return ($curvepoints); } function calc_arrowsize($width,&$map,$linkname) { $arrowlengthfactor=4; $arrowwidthfactor=2; // this is so I can use it in some test code - sorry! if($map !== NULL) { if ($map->links[$linkname]->arrowstyle == 'compact') { $arrowlengthfactor=1; $arrowwidthfactor=1; } if (preg_match('/(\d+) (\d+)/', $map->links[$linkname]->arrowstyle, $matches)) { $arrowlengthfactor=$matches[1]; $arrowwidthfactor=$matches[2]; } } $arrowsize = $width * $arrowlengthfactor; $arrowwidth = $width * $arrowwidthfactor; return( array($arrowsize,$arrowwidth) ); } function draw_straight($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map, $q2_percent=50, $unidirectional=FALSE) { $totaldistance = $curvepoints[count($curvepoints)-1][DISTANCE]; if($unidirectional) { $halfway = $totaldistance; $dirs = array(OUT); $q2_percent = 100; $halfway = $totaldistance * ($q2_percent/100); list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); $spine[OUT] = $curvepoints; } else { // we'll split the spine in half here. # $q2_percent = 50; $halfway = $totaldistance * ($q2_percent/100); $dirs = array(OUT,IN); # $dirs = array(IN); list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); # print "Midpoint is: $totaldistance $halfway $halfwayindex $halfway_x,$halfway_y\n"; $spine[OUT] = array(); $spine[IN] = array(); $npoints = count($curvepoints)-1; for($i=0; $i<=$halfwayindex; $i++) { $spine[OUT] []= $curvepoints[$i]; } // finally, add the actual midpoint $spine[OUT] []= array($halfway_x,$halfway_y, $halfway); // and then from the end to the middle for the other arrow for($i=$npoints; $i>$halfwayindex; $i--) { // copy the original spine, but reversing the distance calculation $spine[IN] []= array($curvepoints[$i][X], $curvepoints[$i][Y], $totaldistance - $curvepoints[$i][DISTANCE]); } // finally, add the actual midpoint $spine[IN] []= array($halfway_x,$halfway_y, $totaldistance - $halfway); } # wm_draw_marker_box($image,$map->selected, $halfway_x, $halfway_y ); // now we have two seperate spines, with distances, so that the arrowhead is the end of each. // (or one, if it's unidir) // so we can loop along the spine for each one as a seperate entity // we calculate the arrow size up here, so that we can decide on the // minimum length for a link. The arrowheads are the limiting factor. list( $arrowsize[IN], $arrowwidth[IN] ) = calc_arrowsize( $widths[IN], $map, $linkname ); list( $arrowsize[OUT], $arrowwidth[OUT] ) = calc_arrowsize( $widths[OUT], $map, $linkname ); // the 1.2 here is empirical. It ought to be 1 in theory. // in practice, a link this short is useless anyway, especially with bwlabels. $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]); foreach ($dirs as $dir) { # draw_spine($image, $spine[$dir],$map->selected); #draw_spine_chain($image, $spine[$dir],$map->selected,3); #print "=================\n$linkname/$dir\n"; #dump_spine($spine[$dir]); $n = count($spine[$dir]) - 1; $l = $spine[$dir][$n][DISTANCE]; #print "L=$l N=$n\n"; // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos $arrowsettings = array(+1, 0, $widths[$dir], 0, $fillcolours[$dir], $outlinecolour, 5); # print "Line is $n points to a distance of $l\n"; if($l < $minimumlength) { warn("Skipping too-short line.\n"); } else { $arrow_d = $l - $arrowsize[$dir]; # print "LENGTHS $l $arrow_d ".$arrowsize[$dir]."\n"; list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($spine[$dir], $arrow_d); # print "POS $pre_mid_x,$pre_mid_y $pre_midindex\n"; $out = array_slice($spine[$dir], 0, $pre_midindex); $out []= array($pre_mid_x, $pre_mid_y, $arrow_d); # wm_draw_marker_diamond($image, $map->selected, $pre_mid_x, $pre_mid_y, 5); # imagearc($image,$pre_mid_x, $pre_mid_y ,15,15,0,360,$map->selected); # imagearc($image,$spine[$dir][$pre_midindex+1][X],$spine[$dir][$pre_midindex+1][Y],20,20,0,360,$map->selected); # imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],20,20,0,360,$map->selected); #imagearc($image,$pre_mid_x,$pre_mid_y,20,20,0,360,$map->selected); #imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],12,12,0,360,$map->selected); $spine[$dir] = $out; $adx=($halfway_x - $pre_mid_x); $ady=($halfway_y - $pre_mid_y); $ll=sqrt(($adx * $adx) + ($ady * $ady)); $anx = $ady / $ll; $any = -$adx / $ll; $ax1 = $pre_mid_x + $widths[$dir] * $anx; $ay1 = $pre_mid_y + $widths[$dir] * $any; $ax2 = $pre_mid_x + $arrowwidth[$dir] * $anx; $ay2 = $pre_mid_y + $arrowwidth[$dir] * $any; $ax3 = $halfway_x; $ay3 = $halfway_y; $ax5 = $pre_mid_x - $widths[$dir] * $anx; $ay5 = $pre_mid_y - $widths[$dir] * $any; $ax4 = $pre_mid_x - $arrowwidth[$dir] * $anx; $ay4 = $pre_mid_y - $arrowwidth[$dir] * $any; # draw_spine($image,$spine[$dir],$map->selected); $simple = simplify_spine($spine[$dir]); $newn = count($simple); # draw_spine($image,$simple,$map->selected); # print "Simplified to $newn points\n"; # if($draw_skeleton) draw_spine_chain($im,$simple,$blue, 12); # draw_spine_chain($image,$simple,$map->selected, 12); # draw_spine_chain($image,$spine[$dir],$map->selected, 10); # draw_spine_chain($image,$simple,$map->selected, 12); # draw_spine($image,$simple,$map->selected); // now do the actual drawing.... $numpoints=0; $numrpoints=0; $finalpoints = array(); $reversepoints = array(); $finalpoints[] = $simple[0][X]; $finalpoints[] = $simple[0][Y]; $numpoints++; $reversepoints[] = $simple[0][X]; $reversepoints[] = $simple[0][Y]; $numrpoints++; // before the main loop, add in the jump out to the corners // if this is the first step, then we need to go from the middle to the outside edge first // ( the loop may not run, but these corners are required) $i = 0; $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]); $n1 = $v1->get_normal(); $finalpoints[] = $simple[$i][X] + $n1->dx*$widths[$dir]; $finalpoints[] = $simple[$i][Y] + $n1->dy*$widths[$dir]; $numpoints++; $reversepoints[] = $simple[$i][X] - $n1->dx*$widths[$dir]; $reversepoints[] = $simple[$i][Y] - $n1->dy*$widths[$dir]; $numrpoints++; $max_start = count($simple)-2; # print "max_start is $max_start\n"; for ($i=0; $i <$max_start; $i++) { $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]); $v2 = new Vector($simple[$i+2][X] - $simple[$i+1][X], $simple[$i+2][Y] - $simple[$i+1][Y]); $n1 = $v1->get_normal(); $n2 = $v2->get_normal(); $capping = FALSE; // figure out the angle between the lines - for very sharp turns, we should do something special // (actually, their normals, but the angle is the same and we need the normals later) $angle = rad2deg(atan2($n2->dy,$n2->dx) - atan2($n1->dy,$n1->dx)); if($angle > 180) $angle -= 360; if($angle < -180) $angle += 360; if(abs($angle)>169) { $capping = TRUE; # print "Would cap. ($angle)\n"; } // $capping = FALSE; // override that for now // now figure out the geometry for where the next corners are list($xi1,$yi1) = line_crossing( $simple[$i][X] + $n1->dx * $widths[$dir], $simple[$i][Y] + $n1->dy * $widths[$dir], $simple[$i+1][X] + $n1->dx * $widths[$dir], $simple[$i+1][Y] + $n1->dy * $widths[$dir], $simple[$i+1][X] + $n2->dx * $widths[$dir], $simple[$i+1][Y] + $n2->dy * $widths[$dir], $simple[$i+2][X] + $n2->dx * $widths[$dir], $simple[$i+2][Y] + $n2->dy * $widths[$dir] ); list($xi2,$yi2) = line_crossing( $simple[$i][X] - $n1->dx * $widths[$dir], $simple[$i][Y] - $n1->dy * $widths[$dir], $simple[$i+1][X] - $n1->dx * $widths[$dir], $simple[$i+1][Y] - $n1->dy * $widths[$dir], $simple[$i+1][X] - $n2->dx * $widths[$dir], $simple[$i+1][Y] - $n2->dy * $widths[$dir], $simple[$i+2][X] - $n2->dx * $widths[$dir], $simple[$i+2][Y] - $n2->dy * $widths[$dir] ); if(!$capping) { $finalpoints[] = $xi1; $finalpoints[] = $yi1; $numpoints++; $reversepoints[] = $xi2; $reversepoints[] = $yi2; $numrpoints++; } else { // in here, we need to decide which is the 'outside' of the corner, // because that's what we flatten. The inside of the corner is left alone. // - depending on the relative angle between the two segments, it could // be either one of these points. list($xi3,$yi3) = line_crossing( $simple[$i][X] + $n1->dx*$widths[$dir], $simple[$i][Y] + $n1->dy*$widths[$dir], $simple[$i+1][X] + $n1->dx*$widths[$dir], $simple[$i+1][Y] + $n1->dy*$widths[$dir], $simple[$i+1][X] - $n2->dx*$widths[$dir], $simple[$i+1][Y] - $n2->dy*$widths[$dir], $simple[$i+2][X] - $n2->dx*$widths[$dir], $simple[$i+2][Y] - $n2->dy*$widths[$dir] ); list($xi4,$yi4) = line_crossing( $simple[$i][X] - $n1->dx*$widths[$dir], $simple[$i][Y] - $n1->dy*$widths[$dir], $simple[$i+1][X] - $n1->dx*$widths[$dir], $simple[$i+1][Y] - $n1->dy*$widths[$dir], $simple[$i+1][X] + $n2->dx*$widths[$dir], $simple[$i+1][Y] + $n2->dy*$widths[$dir], $simple[$i+2][X] + $n2->dx*$widths[$dir], $simple[$i+2][Y] + $n2->dy*$widths[$dir] ); if($angle < 0) { $finalpoints[] = $xi3; $finalpoints[] = $yi3; $numpoints++; $finalpoints[] = $xi4; $finalpoints[] = $yi4; $numpoints++; $reversepoints[] = $xi2; $reversepoints[] = $yi2; $numrpoints++; } else { $reversepoints[] = $xi4; $reversepoints[] = $yi4; $numrpoints++; $reversepoints[] = $xi3; $reversepoints[] = $yi3; $numrpoints++; $finalpoints[] = $xi1; $finalpoints[] = $yi1; $numpoints++; } } } // at this end, we add the arrowhead $finalpoints[] = $ax1; $finalpoints[] = $ay1; $finalpoints[] = $ax2; $finalpoints[] = $ay2; $finalpoints[] = $ax3; $finalpoints[] = $ay3; $finalpoints[] = $ax4; $finalpoints[] = $ay4; $finalpoints[] = $ax5; $finalpoints[] = $ay5; $numpoints += 5; // combine the forwards and backwards paths, to make a complete loop for($i=($numrpoints-1)*2; $i>=0; $i-=2) { $x = $reversepoints[$i]; $y = $reversepoints[$i+1]; $finalpoints[] = $x; $finalpoints[] = $y; $numpoints++; } // $finalpoints[] contains a complete outline of the line at this stage if (!is_null($fillcolours[$dir])) { wimagefilledpolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[4]); } else { debug("Not drawing $linkname ($dir) fill because there is no fill colour\n"); } $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir"; $map->imap->addArea("Polygon", $areaname, '', $finalpoints); debug ("Adding Poly imagemap for $areaname\n"); if (!is_null($outlinecolour)) { wimagepolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[5]); } else { debug("Not drawing $linkname ($dir) outline because there is no outline colour\n"); } } } } // top-level function that takes a two lists to define some points, and draws a weathermap link // - this takes care of all the extras, like arrowheads, and where to put the bandwidth labels // curvepoints is an array of the points the curve passes through // width is the link width (the actual width is twice this) // outlinecolour is a GD colour reference // fillcolours is an array of two more colour references, one for the out, and one for the in spans function draw_curve($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map, $q2_percent=50, $unidirectional=FALSE) { // now we have a 'spine' - all the central points for this curve. // time to flesh it out to the right width, and figure out where to draw arrows and bandwidth boxes... // get the full length of the curve from the last point $totaldistance = $curvepoints[count($curvepoints)-1][2]; // find where the in and out arrows will join (normally halfway point) $halfway = $totaldistance * ($q2_percent/100); $dirs = array(OUT,IN); // for a unidirectional map, we just ignore the second half (direction = -1) if($unidirectional) { $halfway = $totaldistance; $dirs = array(OUT); } // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos $arrowsettings[OUT] = array(+1, 0, $widths[OUT], 0, $fillcolours[OUT], $outlinecolour, 5); $arrowsettings[IN] = array(-1, count($curvepoints) - 1, $widths[IN], 0, $fillcolours[IN], $outlinecolour, 95); // we calculate the arrow size up here, so that we can decide on the // minimum length for a link. The arrowheads are the limiting factor. list($arrowsize[IN],$arrowwidth[IN]) = calc_arrowsize($widths[IN], $map, $linkname); list($arrowsize[OUT],$arrowwidth[OUT]) = calc_arrowsize($widths[OUT], $map, $linkname); // the 1.2 here is empirical. It ought to be 1 in theory. // in practice, a link this short is useless anyway, especially with bwlabels. $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]); # warn("$linkname: Total: $totaldistance $arrowsize $arrowwidth $minimumlength\n"); if($totaldistance <= $minimumlength) { warn("Skipping drawing very short link ($linkname). Impossible to draw! Try changing WIDTH or ARROWSTYLE? [WMWARN01]\n"); return; } list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); // loop over direction here // direction is 1.0 for the first half (forwards through the pointlist), and -1.0 for the second half (backwards from the end) // - used as a multiplier on anything that looks forwards or backwards through the list foreach ($dirs as $dir) { $direction = $arrowsettings[$dir][0]; // $width = $widths[$dir]; // this is the last index before the arrowhead starts list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($curvepoints,$halfway - $direction * $arrowsize[$dir]); $there_points=array(); $back_points=array(); $arrowpoints=array(); # if ($direction < 0) { $start=count($curvepoints) - 1; } # else { $start=0; } $start = $arrowsettings[$dir][1]; for ($i=$start; $i != $pre_midindex; $i+=$direction) { // for each point on the spine, produce two points normal to it's direction, // each is $width away from the spine, but we build up the two lists in the opposite order, // so that when they are joined together, we get one continuous line $dx=$curvepoints[$i + $direction][0] - $curvepoints[$i][0]; $dy=$curvepoints[$i + $direction][1] - $curvepoints[$i][1]; $l=sqrt(($dx * $dx) + ($dy * $dy)); $nx=$dy / $l; $ny=-$dx / $l; $there_points[]=$curvepoints[$i][0] + $direction * $widths[$dir] * $nx; $there_points[]=$curvepoints[$i][1] + $direction * $widths[$dir] * $ny; $back_points[]=$curvepoints[$i][0] - $direction * $widths[$dir] * $nx; $back_points[]=$curvepoints[$i][1] - $direction * $widths[$dir] * $ny; } // all the normal line is done, now lets add an arrowhead on $adx=($halfway_x - $pre_mid_x); $ady=($halfway_y - $pre_mid_y); $l=sqrt(($adx * $adx) + ($ady * $ady)); $anx=$ady / $l; $any=-$adx / $l; $there_points[]=$pre_mid_x + $direction * $widths[$dir] * $anx; $there_points[]=$pre_mid_y + $direction * $widths[$dir] * $any; $there_points[]=$pre_mid_x + $direction * $arrowwidth[$dir] * $anx; $there_points[]=$pre_mid_y + $direction * $arrowwidth[$dir] * $any; $there_points[]=$halfway_x; $there_points[]=$halfway_y; $there_points[]=$pre_mid_x - $direction * $arrowwidth[$dir] * $anx; $there_points[]=$pre_mid_y - $direction * $arrowwidth[$dir] * $any; $there_points[]=$pre_mid_x - $direction * $widths[$dir] * $anx; $there_points[]=$pre_mid_y - $direction * $widths[$dir] * $any; // all points done, now combine the lists, and produce the final result. $metapts = ""; $y=array_pop($back_points); $x=array_pop($back_points); do { $metapts .= " $x $y"; $there_points[]=$x; $there_points[]=$y; $y=array_pop($back_points); $x=array_pop($back_points); } while (!is_null($y)); $arrayindex=1; if ($direction < 0) $arrayindex=0; if (!is_null($fillcolours[$arrayindex])) { wimagefilledpolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][4]); } else { debug("Not drawing $linkname ($dir) fill because there is no fill colour\n"); } # $areaname = "LINK:" . $linkname. ":$dir"; $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir"; $map->imap->addArea("Polygon", $areaname, '', $there_points); debug ("Adding Poly imagemap for $areaname\n"); if (!is_null($outlinecolour)) { wimagepolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][5]); } else { debug("Not drawing $linkname ($dir) outline because there is no outline colour\n"); } } } // Take a spine, and strip out all the points that are co-linear with the points either side of them function simplify_spine(&$input, $epsilon=1e-10) { $output = array(); $output []= $input[0]; $n=1; $c = count($input)-2; $skip=0; for($n=1; $n<=$c; $n++) { $x = $input[$n][X]; $y = $input[$n][Y]; // figure out the area of the triangle formed by this point, and the one before and after $a = abs($input[$n-1][X] * ( $input[$n][Y] - $input[$n+1][Y] ) + $input[$n][X] * ( $input[$n+1][Y] - $input[$n-1][Y] ) + $input[$n+1][X] * ( $input[$n-1][Y] - $input[$n][Y] ) ); # print "$n $x,$y $a"; if ( $a > $epsilon) // if(1==1) { $output []= $input[$n]; # print " KEEP"; } else { // ignore n $skip++; # print " SKIP"; } # print "\n"; } debug("Skipped $skip points of $c\n"); # print "------------------------\n"; $output []= $input[$c+1]; return $output; } function unformat_number($instring, $kilo = 1000) { $matches=0; $number=0; if (preg_match("/([0-9\.]+)(M|G|K|T|m|u)/", $instring, $matches)) { $number=floatval($matches[1]); if ($matches[2] == 'K') { $number=$number * $kilo; } if ($matches[2] == 'M') { $number=$number * $kilo * $kilo; } if ($matches[2] == 'G') { $number=$number * $kilo * $kilo * $kilo; } if ($matches[2] == 'T') { $number=$number * $kilo * $kilo * $kilo * $kilo; } // new, for absolute datastyle. Think seconds. if ($matches[2] == 'm') { $number=$number / $kilo; } if ($matches[2] == 'u') { $number=$number / ($kilo * $kilo); } } else { $number=floatval($instring); } return ($number); } // given a compass-point, and a width & height, return a tuple of the x,y offsets function calc_offset($offsetstring, $width, $height) { if(preg_match("/^([-+]?\d+):([-+]?\d+)$/",$offsetstring,$matches)) { debug("Numeric Offset found\n"); return(array($matches[1],$matches[2])); } elseif(preg_match("/(NE|SE|NW|SW|N|S|E|W|C)(\d+)?$/i",$offsetstring,$matches)) { $multiply = 1; if( isset($matches[2] ) ) { $multiply = intval($matches[2])/100; debug("Percentage compass offset: multiply by $multiply"); } $height = $height * $multiply; $width = $width * $multiply; switch (strtoupper($matches[1])) { case 'N': return (array(0, -$height / 2)); break; case 'S': return (array(0, $height / 2)); break; case 'E': return (array(+$width / 2, 0)); break; case 'W': return (array(-$width / 2, 0)); break; case 'NW': return (array(-$width / 2, -$height / 2)); break; case 'NE': return (array($width / 2, -$height / 2)); break; case 'SW': return (array(-$width / 2, $height / 2)); break; case 'SE': return (array($width / 2, $height / 2)); break; case 'C': default: return (array(0, 0)); break; } } elseif( preg_match("/(-?\d+)r(\d+)$/i",$offsetstring,$matches) ) { $angle = intval($matches[1]); $distance = intval($matches[2]); $x = $distance * sin(deg2rad($angle)); $y = - $distance * cos(deg2rad($angle)); return (array($x,$y)); } else { warn("Got a position offset that didn't make sense ($offsetstring)."); return (array(0, 0)); } } // These next two are based on perl's Number::Format module // by William R. Ward, chopped down to just what I needed function format_number($number, $precision = 2, $trailing_zeroes = 0) { $sign=1; if ($number < 0) { $number=abs($number); $sign=-1; } $number=round($number, $precision); $integer=intval($number); if (strlen($integer) < strlen($number)) { $decimal=substr($number, strlen($integer) + 1); } if (!isset($decimal)) { $decimal=''; } $integer=$sign * $integer; if ($decimal == '') { return ($integer); } else { return ($integer . "." . $decimal); } } function nice_bandwidth($number, $kilo = 1000,$decimals=1,$below_one=TRUE) { $suffix=''; if ($number == 0) return '0'; $mega=$kilo * $kilo; $giga=$mega * $kilo; $tera=$giga * $kilo; $milli = 1/$kilo; $micro = 1/$mega; $nano = 1/$giga; if ($number >= $tera) { $number/=$tera; $suffix="T"; } elseif ($number >= $giga) { $number/=$giga; $suffix="G"; } elseif ($number >= $mega) { $number/=$mega; $suffix="M"; } elseif ($number >= $kilo) { $number/=$kilo; $suffix="K"; } elseif ($number >= 1) { $number = $number; $suffix=""; } elseif (($below_one==TRUE) && ($number >= $milli)) { $number/=$milli; $suffix="m"; } elseif (($below_one==TRUE) && ($number >= $micro)) { $number/=$micro; $suffix="u"; } elseif (($below_one==TRUE) && ($number >= $nano)) { $number/=$nano; $suffix="n"; } $result=format_number($number, $decimals) . $suffix; return ($result); } function nice_scalar($number, $kilo = 1000, $decimals=1) { $suffix = ''; $prefix = ''; if ($number == 0) return '0'; if($number < 0) { $number = -$number; $prefix = '-'; } $mega=$kilo * $kilo; $giga=$mega * $kilo; $tera=$giga * $kilo; if ($number > $tera) { $number/=$tera; $suffix="T"; } elseif ($number > $giga) { $number/=$giga; $suffix="G"; } elseif ($number > $mega) { $number/=$mega; $suffix="M"; } elseif ($number > $kilo) { $number/=$kilo; $suffix="K"; } elseif ($number > 1) { $number = $number; $suffix=""; } elseif ($number < (1 / ($kilo))) { $number=$number * $mega; $suffix="u"; } elseif ($number < 1) { $number=$number * $kilo; $suffix="m"; } $result = $prefix . format_number($number, $decimals) . $suffix; return ($result); } // *********************************************** // we use enough points in various places to make it worth a small class to save some variable-pairs. class Point { var $x, $y; function Point($x=0,$y=0) { $this->x = $x; $this->y = $y; } } // similarly for 2D vectors class Vector { var $dx, $dy; function Vector($dx=0,$dy=0) { $this->dx = $dx; $this->dy = $dy; } function get_normal() { $len = $this->length(); $nx1 = $this->dy / $len; $ny1 = -$this->dx / $len; return( new Vector($nx1, $ny1)); } function normalise() { $len = $this->length(); $this->dx = $this->dx/$len; $this->dy = $this->dy/$len; } function length() { return( sqrt(($this->dx)*($this->dx) + ($this->dy)*($this->dy)) ); } } class Colour { var $r,$g,$b, $alpha; // take in an existing value and create a Colour object for it function Colour() { if(func_num_args() == 3) # a set of 3 colours { $this->r = func_get_arg(0); # r $this->g = func_get_arg(1); # g $this->b = func_get_arg(2); # b #print "3 args"; #print $this->as_string()."--"; } if( (func_num_args() == 1) && gettype(func_get_arg(0))=='array' ) # an array of 3 colours { #print "1 args"; $ary = func_get_arg(0); $this->r = $ary[0]; $this->g = $ary[1]; $this->b = $ary[2]; } } // Is this a transparent/none colour? function is_real() { if($this->r >= 0 && $this->g >=0 && $this->b >= 0) { return true; } else { return false; } } // Is this a transparent/none colour? function is_none() { if($this->r == -1 && $this->g == -1 && $this->b == -1) { return true; } else { return false; } } // Is this a contrast colour? function is_contrast() { if($this->r == -3 && $this->g == -3 && $this->b == -3) { return true; } else { return false; } } // Is this a copy colour? function is_copy() { if($this->r == -2 && $this->g == -2 && $this->b == -2) { return true; } else { return false; } } // allocate a colour in the appropriate image context // - things like scale colours are used in multiple images now (the scale, several nodes, the main map...) function gdallocate($image_ref) { if($this->is_none()) { return NULL; } else { return(myimagecolorallocate($image_ref, $this->r, $this->g, $this->b)); } } // based on an idea from: http://www.bennadel.com/index.cfm?dax=blog:902.view function contrast_ary() { if( (($this->r + $this->g + $this->b) > 500) || ($this->g > 140) ) { return( array(0,0,0) ); } else { return( array(255,255,255) ); } } function contrast() { return( new Colour($this->contrast_ary() ) ); } // make a printable version, for debugging // - optionally take a format string, so we can use it for other things (like WriteConfig, or hex in stylesheets) function as_string($format = "RGB(%d,%d,%d)") { return (sprintf($format, $this->r, $this->g, $this->b)); } function as_config() { return $this->as_string("%d %d %d"); } function as_html() { if($this->is_real()) { return $this->as_string("#%02x%02x%02x"); } else { return ""; } } } // A series of wrapper functions around all the GD function calls // - I added these in so I could make a 'metafile' easily of all the // drawing commands for a map. I have a basic Perl-Cairo script that makes // anti-aliased maps from these, using Cairo instead of GD. function metadump($string, $truncate=FALSE) { // comment this line to get a metafile for this map return; if($truncate) { $fd = fopen("metadump.txt","w+"); } else { $fd = fopen("metadump.txt","a"); } fputs($fd,$string."\n"); fclose($fd); } function metacolour(&$col) { return ($col['red1']." ".$col['green1']." ".$col['blue1']); } function wimagecreate($width,$height) { metadump("NEWIMAGE $width $height"); return(imagecreate($width,$height)); } function wimagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color ) { if ($color===NULL) return; $col = imagecolorsforindex($image, $color); $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; metadump("FRECT $x1 $y1 $x2 $y2 $r $g $b $a"); return(imagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color )); } function wimagerectangle( $image ,$x1, $y1, $x2, $y2, $color ) { if ($color===NULL) return; $col = imagecolorsforindex($image, $color); $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; metadump("RECT $x1 $y1 $x2 $y2 $r $g $b $a"); return(imagerectangle( $image ,$x1, $y1, $x2, $y2, $color )); } function wimagepolygon($image, $points, $num_points, $color) { if ($color===NULL) return; $col = imagecolorsforindex($image, $color); $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; $pts = ""; for ($i=0; $i < $num_points; $i++) { $pts .= $points[$i * 2]." "; $pts .= $points[$i * 2+1]." "; } metadump("POLY $num_points ".$pts." $r $g $b $a"); return(imagepolygon($image, $points, $num_points, $color)); } function wimagefilledpolygon($image, $points, $num_points, $color) { if ($color===NULL) return; $col = imagecolorsforindex($image, $color); $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; $pts = ""; for ($i=0; $i < $num_points; $i++) { $pts .= $points[$i * 2]." "; $pts .= $points[$i * 2+1]." "; } metadump("FPOLY $num_points ".$pts." $r $g $b $a"); return(imagefilledpolygon($image, $points, $num_points, $color)); } function wimagecreatetruecolor($width, $height) { metadump("BLANKIMAGE $width $height"); return imagecreatetruecolor($width,$height); } function wimagettftext($image, $size, $angle, $x, $y, $color, $file, $string) { if ($color===NULL) return; $col = imagecolorsforindex($image, $color); $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; metadump("TEXT $x $y $angle $size $file $r $g $b $a $string"); return(imagettftext($image, $size, $angle, $x, $y, $color, $file, $string)); } function wm_draw_marker_diamond($im, $col, $x, $y, $size=10) { $points = array(); $points []= $x-$size; $points []= $y; $points []= $x; $points []= $y-$size; $points []= $x+$size; $points []= $y; $points []= $x; $points []= $y+$size; $num_points = 4; imagepolygon($im, $points, $num_points, $col); } function wm_draw_marker_box($im, $col, $x, $y, $size=10) { $points = array(); $points []= $x-$size; $points []= $y-$size; $points []= $x+$size; $points []= $y-$size; $points []= $x+$size; $points []= $y+$size; $points []= $x-$size; $points []= $y+$size; $num_points = 4; imagepolygon($im, $points, $num_points, $col); } function wm_draw_marker_circle($im, $col, $x, $y, $size=10) { imagearc($im,$x, $y ,$size,$size,0,360,$col); } function draw_spine_chain($im,$spine,$col, $size=10) { $newn = count($spine); for ($i=0; $i < $newn; $i++) { imagearc($im,$spine[$i][X],$spine[$i][Y],$size,$size,0,360,$col); } } function dump_spine($spine) { print "===============\n"; for($i=0; $i WebSVN - weathermap - Blame - Rev 117 - /WeatherMap.functions.php
  jablonka.czprosek.czf

weathermap

Subversion Repositories:
[/] [WeatherMap.functions.php] - Blame information for rev 117

 

Line No. Rev Author Line

Powered by WebSVN 2.2.1