1 | 85 | simandl | <?php |
2 | | | // PHP Weathermap 0.97a |
3 | | | // Copyright Howard Jones, 2005-2010 howie@thingy.com |
4 | | | // http://www.network-weathermap.com/ |
5 | | | // Released under the GNU Public License |
6 | | | |
7 | | | // Utility functions |
8 | | | // Check for GD & PNG support This is just in here so that both the editor and CLI can use it without the need for another file |
9 | | | function module_checks() |
10 | | | { |
11 | | | if (!extension_loaded('gd')) |
12 | | | { |
13 | | | warn ("\n\nNo image (gd) extension is loaded. This is required by weathermap. [WMWARN20]\n\n"); |
14 | | | warn ("\nrun check.php to check PHP requirements.\n\n"); |
15 | | | |
16 | | | return (FALSE); |
17 | | | } |
18 | | | |
19 | | | if (!function_exists('imagecreatefrompng')) |
20 | | | { |
21 | | | warn ("Your GD php module doesn't support PNG format. [WMWARN21]\n"); |
22 | | | warn ("\nrun check.php to check PHP requirements.\n\n"); |
23 | | | return (FALSE); |
24 | | | } |
25 | | | |
26 | | | if (!function_exists('imagecreatetruecolor')) |
27 | | | { |
28 | | | warn ("Your GD php module doesn't support truecolor. [WMWARN22]\n"); |
29 | | | warn ("\nrun check.php to check PHP requirements.\n\n"); |
30 | | | return (FALSE); |
31 | | | } |
32 | | | |
33 | | | if (!function_exists('imagecopyresampled')) |
34 | | | { |
35 | | | warn ("Your GD php module doesn't support thumbnail creation (imagecopyresampled). [WMWARN23]\n"); |
36 | | | } |
37 | | | return (TRUE); |
38 | | | } |
39 | | | |
40 | | | function debug($string) |
41 | | | { |
42 | | | global $weathermap_debugging; |
43 | | | global $weathermap_map; |
44 | | | global $weathermap_debug_suppress; |
45 | | | |
46 | | | if ($weathermap_debugging) |
47 | | | { |
48 | | | $calling_fn = ""; |
49 | | | if(function_exists("debug_backtrace")) |
50 | | | { |
51 | | | $bt = debug_backtrace(); |
52 | | | $index = 1; |
53 | | | # $class = (isset($bt[$index]['class']) ? $bt[$index]['class'] : ''); |
54 | | | $function = (isset($bt[$index]['function']) ? $bt[$index]['function'] : ''); |
55 | | | $index = 0; |
56 | | | $file = (isset($bt[$index]['file']) ? basename($bt[$index]['file']) : ''); |
57 | | | $line = (isset($bt[$index]['line']) ? $bt[$index]['line'] : ''); |
58 | | | |
59 | | | $calling_fn = " [$function@$file:$line]"; |
60 | | | |
61 | | | if(is_array($weathermap_debug_suppress) && in_array(strtolower($function),$weathermap_debug_suppress)) return; |
62 | | | } |
63 | | | |
64 | | | // use Cacti's debug log, if we are running from the poller |
65 | | | if (function_exists('debug_log_insert') && (!function_exists('show_editor_startpage'))) |
66 | | | { cacti_log("DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . rtrim($string), true, "WEATHERMAP"); } |
67 | | | else |
68 | | | { |
69 | | | $stderr=fopen('php://stderr', 'w'); |
70 | | | fwrite($stderr, "DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . $string); |
71 | | | fclose ($stderr); |
72 | | | |
73 | | | // mostly this is overkill, but it's sometimes useful (mainly in the editor) |
74 | | | if(1==0) |
75 | | | { |
76 | | | $log=fopen('debug.log', 'a'); |
77 | | | fwrite($log, "DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . $string); |
78 | | | fclose ($log); |
79 | | | } |
80 | | | } |
81 | | | } |
82 | | | } |
83 | | | |
84 | | | function warn($string,$notice_only=FALSE) |
85 | | | { |
86 | | | global $weathermap_map; |
87 | | | global $weathermap_warncount; |
88 | | | |
89 | | | $message = ""; |
90 | | | |
91 | | | if(!$notice_only) |
92 | | | { |
93 | | | $weathermap_warncount++; |
94 | | | $message .= "WARNING: "; |
95 | | | } |
96 | | | |
97 | | | $message .= ($weathermap_map==''?'':$weathermap_map.": ") . rtrim($string); |
98 | | | |
99 | | | // use Cacti's debug log, if we are running from the poller |
100 | | | if (function_exists('cacti_log') && (!function_exists('show_editor_startpage'))) |
101 | | | { cacti_log($message, true, "WEATHERMAP"); } |
102 | | | else |
103 | | | { |
104 | | | $stderr=fopen('php://stderr', 'w'); |
105 | | | fwrite($stderr, $message."\n"); |
106 | | | fclose ($stderr); |
107 | | | } |
108 | | | } |
109 | | | |
110 | | | function js_escape($str, $wrap=TRUE) |
111 | | | { |
112 | | | $str=str_replace('\\', '\\\\', $str); |
113 | | | $str=str_replace('"', '\\"', $str); |
114 | | | |
115 | | | if($wrap) $str='"' . $str . '"'; |
116 | | | |
117 | | | return ($str); |
118 | | | } |
119 | | | |
120 | | | function mysprintf($format,$value,$kilo=1000) |
121 | | | { |
122 | | | $output = ""; |
123 | | | |
124 | | | debug("mysprintf: $format $value\n"); |
125 | | | if(preg_match("/%(\d*\.?\d*)k/",$format,$matches)) |
126 | | | { |
127 | | | $spec = $matches[1]; |
128 | | | $places = 2; |
129 | | | if($spec !='') |
130 | | | { |
131 | | | preg_match("/(\d*)\.?(\d*)/",$spec,$matches); |
132 | | | if($matches[2] != '') $places=$matches[2]; |
133 | | | // we don't really need the justification (pre-.) part... |
134 | | | } |
135 | | | debug("KMGT formatting $value with $spec.\n"); |
136 | | | $result = nice_scalar($value, $kilo, $places); |
137 | | | $output = preg_replace("/%".$spec."k/",$format,$result); |
138 | | | } |
139 | | | else |
140 | | | { |
141 | | | debug("Falling through to standard sprintf\n"); |
142 | | | $output = sprintf($format,$value); |
143 | | | } |
144 | | | return $output; |
145 | | | } |
146 | | | |
147 | | | // ParseString is based on code from: |
148 | | | // http://www.webscriptexpert.com/Php/Space-Separated%20Tag%20Parser/ |
149 | | | |
150 | | | function ParseString($input) |
151 | | | { |
152 | | | $output = array(); // Array of Output |
153 | | | $cPhraseQuote = null; // Record of the quote that opened the current phrase |
154 | | | $sPhrase = null; // Temp storage for the current phrase we are building |
155 | | | |
156 | | | // Define some constants |
157 | | | $sTokens = " \t"; // Space, Tab |
158 | | | $sQuotes = "'\""; // Single and Double Quotes |
159 | | | |
160 | | | // Start the State Machine |
161 | | | do |
162 | | | { |
163 | | | // Get the next token, which may be the first |
164 | | | $sToken = isset($sToken)? strtok($sTokens) : strtok($input, $sTokens); |
165 | | | |
166 | | | // Are there more tokens? |
167 | | | if ($sToken === false) |
168 | | | { |
169 | | | // Ensure that the last phrase is marked as ended |
170 | | | $cPhraseQuote = null; |
171 | | | } |
172 | | | else |
173 | | | { |
174 | | | // Are we within a phrase or not? |
175 | | | if ($cPhraseQuote !== null) |
176 | | | { |
177 | | | // Will the current token end the phrase? |
178 | | | if (substr($sToken, -1, 1) === $cPhraseQuote) |
179 | | | { |
180 | | | // Trim the last character and add to the current phrase, with a single leading space if necessary |
181 | | | if (strlen($sToken) > 1) $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . substr($sToken, 0, -1); |
182 | | | $cPhraseQuote = null; |
183 | | | } |
184 | | | else |
185 | | | { |
186 | | | // If not, add the token to the phrase, with a single leading space if necessary |
187 | | | $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . $sToken; |
188 | | | } |
189 | | | } |
190 | | | else |
191 | | | { |
192 | | | // Will the current token start a phrase? |
193 | | | if (strpos($sQuotes, $sToken[0]) !== false) |
194 | | | { |
195 | | | // Will the current token end the phrase? |
196 | | | if ((strlen($sToken) > 1) && ($sToken[0] === substr($sToken, -1, 1))) |
197 | | | { |
198 | | | // The current token begins AND ends the phrase, trim the quotes |
199 | | | $sPhrase = substr($sToken, 1, -1); |
200 | | | } |
201 | | | else |
202 | | | { |
203 | | | // Remove the leading quote |
204 | | | $sPhrase = substr($sToken, 1); |
205 | | | $cPhraseQuote = $sToken[0]; |
206 | | | } |
207 | | | } |
208 | | | else |
209 | | | $sPhrase = $sToken; |
210 | | | } |
211 | | | } |
212 | | | |
213 | | | // If, at this point, we are not within a phrase, the prepared phrase is complete and can be added to the array |
214 | | | if (($cPhraseQuote === null) && ($sPhrase != null)) |
215 | | | { |
216 | | | $output[] = $sPhrase; |
217 | | | $sPhrase = null; |
218 | | | } |
219 | | | } |
220 | | | while ($sToken !== false); // Stop when we receive FALSE from strtok() |
221 | | | |
222 | | | return $output; |
223 | | | } |
224 | | | |
225 | | | // wrapper around imagecolorallocate to try and re-use palette slots where possible |
226 | | | function myimagecolorallocate($image, $red, $green, $blue) |
227 | | | { |
228 | | | // it's possible that we're being called early - just return straight away, in that case |
229 | | | if(!isset($image)) return(-1); |
230 | | | |
231 | | | $existing=imagecolorexact($image, $red, $green, $blue); |
232 | | | |
233 | | | if ($existing > -1) |
234 | | | return $existing; |
235 | | | |
236 | | | return (imagecolorallocate($image, $red, $green, $blue)); |
237 | | | } |
238 | | | |
239 | | | function screenshotify($input) |
240 | | | { |
241 | | | $tmp = $input; |
242 | | | |
243 | | | $tmp = preg_replace("/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/","127.0.0.1",$tmp); |
244 | | | $tmp = preg_replace("/([A-Za-z]{3,})/e","str_repeat('x',strlen('\\1'))",$tmp); |
245 | | | |
246 | | | return($tmp); |
247 | | | } |
248 | | | |
249 | | | function render_colour($col) |
250 | | | { |
251 | | | if (($col[0] == -1) && ($col[1] == -1) && ($col[1] == -1)) { return 'none'; } |
252 | | | else if (($col[0] == -2) && ($col[1] == -2) && ($col[1] == -2)) { return 'copy'; } |
253 | | | else if (($col[0] == -3) && ($col[1] == -3) && ($col[1] == -3)) { return 'contrast'; } |
254 | | | else { return sprintf("%d %d %d", $col[0], $col[1], $col[2]); } |
255 | | | } |
256 | | | |
257 | | | // take the same set of points that imagepolygon does, but don't close the shape |
258 | | | function imagepolyline($image, $points, $npoints, $color) |
259 | | | { |
260 | | | for ($i=0; $i < ($npoints - 1); $i++) |
261 | | | { |
262 | | | imageline($image, $points[$i * 2], $points[$i * 2 + 1], $points[$i * 2 + 2], $points[$i * 2 + 3], |
263 | | | $color); |
264 | | | } |
265 | | | } |
266 | | | |
267 | | | // draw a filled round-cornered rectangle |
268 | | | function imagefilledroundedrectangle($image , $x1 , $y1 , $x2 , $y2 , $radius, $color) |
269 | | | { |
270 | | | imagefilledrectangle($image, $x1,$y1+$radius, $x2,$y2-$radius, $color); |
271 | | | imagefilledrectangle($image, $x1+$radius,$y1, $x2-$radius,$y2, $color); |
272 | | | |
273 | | | imagefilledarc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); |
274 | | | imagefilledarc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); |
275 | | | |
276 | | | imagefilledarc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); |
277 | | | imagefilledarc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE); |
278 | | | |
279 | | | # bool imagefilledarc ( resource $image , int $cx , int $cy , int $width , int $height , int $start , int $end , int $color , int $style ) |
280 | | | } |
281 | | | |
282 | | | // draw a round-cornered rectangle |
283 | | | function imageroundedrectangle( $image , $x1 , $y1 , $x2 , $y2 , $radius, $color ) |
284 | | | { |
285 | | | |
286 | | | imageline($image, $x1+$radius, $y1, $x2-$radius, $y1, $color); |
287 | | | imageline($image, $x1+$radius, $y2, $x2-$radius, $y2, $color); |
288 | | | imageline($image, $x1, $y1+$radius, $x1, $y2-$radius, $color); |
289 | | | imageline($image, $x2, $y1+$radius, $x2, $y2-$radius, $color); |
290 | | | |
291 | | | imagearc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 180, 270, $color); |
292 | | | imagearc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 270, 360, $color); |
293 | | | imagearc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 90, 180, $color); |
294 | | | imagearc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 90, $color); |
295 | | | } |
296 | | | |
297 | | | function imagecreatefromfile($filename) |
298 | | | { |
299 | | | $bgimage=NULL; |
300 | | | $formats = imagetypes(); |
301 | | | if (is_readable($filename)) |
302 | | | { |
303 | | | list($width, $height, $type, $attr) = getimagesize($filename); |
304 | | | switch($type) |
305 | | | { |
306 | | | case IMAGETYPE_GIF: |
307 | | | if(imagetypes() & IMG_GIF) |
308 | | | { |
309 | | | $bgimage=imagecreatefromgif($filename); |
310 | | | } |
311 | | | else |
312 | | | { |
313 | | | warn("Image file $filename is GIF, but GIF is not supported by your GD library. [WMIMG01]\n"); |
314 | | | } |
315 | | | break; |
316 | | | |
317 | | | case IMAGETYPE_JPEG: |
318 | | | if(imagetypes() & IMG_JPEG) |
319 | | | { |
320 | | | $bgimage=imagecreatefromjpeg($filename); |
321 | | | } |
322 | | | else |
323 | | | { |
324 | | | warn("Image file $filename is JPEG, but JPEG is not supported by your GD library. [WMIMG02]\n"); |
325 | | | } |
326 | | | break; |
327 | | | |
328 | | | case IMAGETYPE_PNG: |
329 | | | if(imagetypes() & IMG_PNG) |
330 | | | { |
331 | | | $bgimage=imagecreatefrompng($filename); |
332 | | | } |
333 | | | else |
334 | | | { |
335 | | | warn("Image file $filename is PNG, but PNG is not supported by your GD library. [WMIMG03]\n"); |
336 | | | } |
337 | | | break; |
338 | | | |
339 | | | default: |
340 | | | warn("Image file $filename wasn't recognised (type=$type). Check format is supported by your GD library. [WMIMG04]\n"); |
341 | | | break; |
342 | | | } |
343 | | | } |
344 | | | else |
345 | | | { |
346 | | | warn("Image file $filename is unreadable. Check permissions. [WMIMG05]\n"); |
347 | | | } |
348 | | | return $bgimage; |
349 | | | } |
350 | | | |
351 | | | // taken from here: |
352 | | | // http://www.php.net/manual/en/function.imagefilter.php#62395 |
353 | | | // ( with some bugfixes and changes) |
354 | | | // |
355 | | | // Much nicer colorization than imagefilter does, AND no special requirements. |
356 | | | // Preserves white, black and transparency. |
357 | | | // |
358 | | | function imagecolorize($im, $r, $g, $b) |
359 | | | { |
360 | | | //We will create a monochromatic palette based on |
361 | | | //the input color |
362 | | | //which will go from black to white |
363 | | | //Input color luminosity: this is equivalent to the |
364 | | | //position of the input color in the monochromatic |
365 | | | //palette |
366 | | | $lum_inp = round(255 * ($r + $g + $b) / 765); //765=255*3 |
367 | | | |
368 | | | //We fill the palette entry with the input color at its |
369 | | | //corresponding position |
370 | | | |
371 | | | $pal[$lum_inp]['r'] = $r; |
372 | | | $pal[$lum_inp]['g'] = $g; |
373 | | | $pal[$lum_inp]['b'] = $b; |
374 | | | |
375 | | | //Now we complete the palette, first we'll do it to |
376 | | | //the black,and then to the white. |
377 | | | |
378 | | | //FROM input to black |
379 | | | //=================== |
380 | | | //how many colors between black and input |
381 | | | $steps_to_black = $lum_inp; |
382 | | | |
383 | | | //The step size for each component |
384 | | | if ($steps_to_black) |
385 | | | { |
386 | | | $step_size_red = $r / $steps_to_black; |
387 | | | $step_size_green = $g / $steps_to_black; |
388 | | | $step_size_blue = $b / $steps_to_black; |
389 | | | } |
390 | | | |
391 | | | for ($i = $steps_to_black; $i >= 0; $i--) |
392 | | | { |
393 | | | $pal[$steps_to_black - $i]['r'] = $r - round($step_size_red * $i); |
394 | | | $pal[$steps_to_black - $i]['g'] = $g - round($step_size_green * $i); |
395 | | | $pal[$steps_to_black - $i]['b'] = $b - round($step_size_blue * $i); |
396 | | | } |
397 | | | |
398 | | | //From input to white: |
399 | | | //=================== |
400 | | | //how many colors between input and white |
401 | | | $steps_to_white = 255 - $lum_inp; |
402 | | | |
403 | | | if ($steps_to_white) |
404 | | | { |
405 | | | $step_size_red = (255 - $r) / $steps_to_white; |
406 | | | $step_size_green = (255 - $g) / $steps_to_white; |
407 | | | $step_size_blue = (255 - $b) / $steps_to_white; |
408 | | | } |
409 | | | else |
410 | | | $step_size_red = $step_size_green = $step_size_blue = 0; |
411 | | | |
412 | | | //The step size for each component |
413 | | | for ($i = ($lum_inp + 1); $i <= 255; $i++) |
414 | | | { |
415 | | | $pal[$i]['r'] = $r + round($step_size_red * ($i - $lum_inp)); |
416 | | | $pal[$i]['g'] = $g + round($step_size_green * ($i - $lum_inp)); |
417 | | | $pal[$i]['b'] = $b + round($step_size_blue * ($i - $lum_inp)); |
418 | | | } |
419 | | | |
420 | | | //--- End of palette creation |
421 | | | |
422 | | | //Now,let's change the original palette into the one we |
423 | | | //created |
424 | | | for ($c = 0; $c < imagecolorstotal($im); $c++) |
425 | | | { |
426 | | | $col = imagecolorsforindex($im, $c); |
427 | | | $lum_src = round(255 * ($col['red'] + $col['green'] + $col['blue']) / 765); |
428 | | | $col_out = $pal[$lum_src]; |
429 | | | |
430 | | | # printf("%d (%d,%d,%d) -> %d -> (%d,%d,%d)\n", $c, |
431 | | | # $col['red'], $col['green'], $col['blue'], |
432 | | | # $lum_src, |
433 | | | # $col_out['r'], $col_out['g'], $col_out['b'] |
434 | | | # ); |
435 | | | |
436 | | | imagecolorset($im, $c, $col_out['r'], $col_out['g'], $col_out['b']); |
437 | | | } |
438 | | | |
439 | | | return($im); |
440 | | | } |
441 | | | |
442 | | | // find the point where a line from x1,y1 through x2,y2 crosses another line through x3,y3 and x4,y4 |
443 | | | // (the point might not be between those points, but beyond them) |
444 | | | // - doesn't handle parallel lines. In our case we will never get them. |
445 | | | // - make sure we remove colinear points, or this will not be true! |
446 | | | function line_crossing($x1,$y1,$x2,$y2, $x3,$y3,$x4,$y4) |
447 | | | { |
448 | | | |
449 | | | // First, check that the slope isn't infinite. |
450 | | | // if it is, tweak it to be merely huge |
451 | | | if($x1 != $x2) { $slope1 = ($y2-$y1)/($x2-$x1); } |
452 | | | else { $slope1 = 1e10; debug("Slope1 is infinite.\n");} |
453 | | | |
454 | | | if($x3 != $x4) { $slope2 = ($y4-$y3)/($x4-$x3); } |
455 | | | else { $slope2 = 1e10; debug("Slope2 is infinite.\n");} |
456 | | | |
457 | | | $a1 = $slope1; |
458 | | | $a2 = $slope2; |
459 | | | $b1 = -1; |
460 | | | $b2 = -1; |
461 | | | $c1 = ($y1 - $slope1 * $x1 ); |
462 | | | $c2 = ($y3 - $slope2 * $x3 ); |
463 | | | |
464 | | | $det_inv = 1/($a1*$b2 - $a2*$b1); |
465 | | | |
466 | | | $xi = (($b1*$c2 - $b2*$c1)*$det_inv); |
467 | | | $yi = (($a2*$c1 - $a1*$c2)*$det_inv); |
468 | | | |
469 | | | return(array($xi,$yi)); |
470 | | | } |
471 | | | |
472 | | | // rotate a list of points around cx,cy by an angle in radians, IN PLACE |
473 | | | function RotateAboutPoint(&$points, $cx,$cy, $angle=0) |
474 | | | { |
475 | | | $npoints = count($points)/2; |
476 | | | |
477 | | | for($i=0;$i<$npoints;$i++) |
478 | | | { |
479 | | | $ox = $points[$i*2] - $cx; |
480 | | | $oy = $points[$i*2+1] - $cy; |
481 | | | $rx = $ox * cos($angle) - $oy*sin($angle); |
482 | | | $ry = $oy * cos($angle) + $ox*sin($angle); |
483 | | | |
484 | | | $points[$i*2] = $rx + $cx; |
485 | | | $points[$i*2+1] = $ry + $cy; |
486 | | | } |
487 | | | } |
488 | | | |
489 | | | // calculate the points for a span of the curve. We pass in the distance so far, and the array index, so that |
490 | | | // the chunk of array generated by this function can be array_merged with existing points from before. |
491 | | | // Considering how many array functions there are, PHP has horrible list support |
492 | | | // 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 |
493 | | | function calculate_catmull_rom_span($startn, $startdistance, $numsteps, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) |
494 | | | { |
495 | | | $Ap_x=-$x0 + 3 * $x1 - 3 * $x2 + $x3; |
496 | | | $Bp_x=2 * $x0 - 5 * $x1 + 4 * $x2 - $x3; |
497 | | | $Cp_x=-$x0 + $x2; |
498 | | | $Dp_x=2 * $x1; |
499 | | | |
500 | | | $Ap_y=-$y0 + 3 * $y1 - 3 * $y2 + $y3; |
501 | | | $Bp_y=2 * $y0 - 5 * $y1 + 4 * $y2 - $y3; |
502 | | | $Cp_y=-$y0 + $y2; |
503 | | | $Dp_y=2 * $y1; |
504 | | | |
505 | | | $d=2; |
506 | | | $n=$startn; |
507 | | | $distance=$startdistance; |
508 | | | |
509 | | | $lx=$x0; |
510 | | | $ly=$y0; |
511 | | | |
512 | | | $allpoints[]=array |
513 | | | ( |
514 | | | $x0, |
515 | | | $y0, |
516 | | | $distance |
517 | | | ); |
518 | | | |
519 | | | for ($i=0; $i <= $numsteps; $i++) |
520 | | | { |
521 | | | $t=$i / $numsteps; |
522 | | | $t2=$t * $t; |
523 | | | $t3=$t2 * $t; |
524 | | | $x=(($Ap_x * $t3) + ($Bp_x * $t2) + ($Cp_x * $t) + $Dp_x) / $d; |
525 | | | $y=(($Ap_y * $t3) + ($Bp_y * $t2) + ($Cp_y * $t) + $Dp_y) / $d; |
526 | | | |
527 | | | if ($i > 0) |
528 | | | { |
529 | | | $step=sqrt((($x - $lx) * ($x - $lx)) + (($y - $ly) * ($y - $ly))); |
530 | | | $distance=$distance + $step; |
531 | | | $allpoints[$n]=array |
532 | | | ( |
533 | | | $x, |
534 | | | $y, |
535 | | | $distance |
536 | | | ); |
537 | | | |
538 | | | $n++; |
539 | | | } |
540 | | | |
541 | | | $lx=$x; |
542 | | | $ly=$y; |
543 | | | } |
544 | | | |
545 | | | return array($allpoints, $distance, $n); |
546 | | | } |
547 | | | |
548 | | | function find_distance_coords(&$pointarray,$distance) |
549 | | | { |
550 | | | // We find the nearest lower point for each distance, |
551 | | | // then linearly interpolate to get a more accurate point |
552 | | | // this saves having quite so many points-per-curve |
553 | | | |
554 | | | $index=find_distance($pointarray, $distance); |
555 | | | |
556 | | | $ratio=($distance - $pointarray[$index][2]) / ($pointarray[$index + 1][2] - $pointarray[$index][2]); |
557 | | | $x = $pointarray[$index][0] + $ratio * ($pointarray[$index + 1][0] - $pointarray[$index][0]); |
558 | | | $y = $pointarray[$index][1] + $ratio * ($pointarray[$index + 1][1] - $pointarray[$index][1]); |
559 | | | |
560 | | | return(array($x,$y,$index)); |
561 | | | } |
562 | | | |
563 | | | function find_distance_coords_angle(&$pointarray,$distance) |
564 | | | { |
565 | | | // This is the point we need |
566 | | | list($x,$y,$index) = find_distance_coords($pointarray,$distance); |
567 | | | |
568 | | | // now to find one either side of it, to get a line to find the angle of |
569 | | | $left = $index; |
570 | | | $right = $left+1; |
571 | | | $max = count($pointarray)-1; |
572 | | | // if we're right up against the last point, then step backwards one |
573 | | | if($right>=$max) |
574 | | | { |
575 | | | $left--; |
576 | | | $right--; |
577 | | | } |
578 | | | # if($left<=0) { $left = 0; } |
579 | | | |
580 | | | $x1 = $pointarray[$left][0]; |
581 | | | $y1 = $pointarray[$left][1]; |
582 | | | |
583 | | | $x2 = $pointarray[$right][0]; |
584 | | | $y2 = $pointarray[$right][1]; |
585 | | | |
586 | | | $dx = $x2 - $x1; |
587 | | | $dy = $y2 - $y1; |
588 | | | |
589 | | | $angle = rad2deg(atan2(-$dy,$dx)); |
590 | | | |
591 | | | return(array($x,$y,$index,$angle)); |
592 | | | } |
593 | | | |
594 | | | // return the index of the point either at (unlikely) or just before the target distance |
595 | | | // we will linearly interpolate afterwards to get a true point - pointarray is an array of 3-tuples produced by the function above |
596 | | | function find_distance(&$pointarray, $distance) |
597 | | | { |
598 | | | $left=0; |
599 | | | $right=count($pointarray) - 1; |
600 | | | |
601 | | | if ($left == $right) |
602 | | | return ($left); |
603 | | | |
604 | | | // if the distance is zero, there's no need to search (and it doesn't work anyway) |
605 | | | if($distance==0) return($left); |
606 | | | |
607 | | | // if it's a point past the end of the line, then just return the end of the line |
608 | | | // Weathermap should *never* ask for this, anyway |
609 | | | if ($pointarray[$right][2] < $distance) { return ($right); } |
610 | | | |
611 | | | // if somehow we have a 0-length curve, then don't try and search, just give up |
612 | | | // in a somewhat predictable manner |
613 | | | if ($pointarray[$left][2] == $pointarray[$right][2]) { return ($left); } |
614 | | | |
615 | | | while ($left <= $right) |
616 | | | { |
617 | | | $mid=floor(($left + $right) / 2); |
618 | | | |
619 | | | if (($pointarray[$mid][2] < $distance) && ($pointarray[$mid + 1][2] >= $distance)) { return $mid; } |
620 | | | |
621 | | | if ($distance <= $pointarray[$mid][2]) { $right=$mid - 1; } |
622 | | | else { $left=$mid + 1; } |
623 | | | } |
624 | | | |
625 | | | print "FELL THROUGH\n"; |
626 | | | die ("Howie's crappy binary search is wrong after all.\n"); |
627 | | | } |
628 | | | |
629 | | | // Give a list of key points, calculate a curve through them |
630 | | | // return value is an array of triples (x,y,distance) |
631 | | | function calc_curve(&$in_xarray, &$in_yarray,$pointsperspan = 32) |
632 | | | { |
633 | | | // search through the point list, for consecutive duplicate points |
634 | | | // (most common case will be a straight link with both NODEs at the same place, I think) |
635 | | | // strip those out, because they'll break the binary search/centre-point stuff |
636 | | | |
637 | | | $last_x=NULL; |
638 | | | $last_y=NULL; |
639 | | | |
640 | | | for ($i=0; $i < count($in_xarray); $i++) |
641 | | | { |
642 | | | if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug |
643 | | | ("Dumping useless duplicate point on curve\n"); } |
644 | | | else |
645 | | | { |
646 | | | $xarray[]=$in_xarray[$i]; |
647 | | | $yarray[]=$in_yarray[$i]; |
648 | | | } |
649 | | | |
650 | | | $last_x=$in_xarray[$i]; |
651 | | | $last_y=$in_yarray[$i]; |
652 | | | } |
653 | | | |
654 | | | // only proceed if we still have at least two points! |
655 | | | if(count($xarray) <= 1) |
656 | | | { |
657 | | | warn ("Arrow not drawn, as it's 1-dimensional.\n"); |
658 | | | return (array(NULL, NULL, NULL, NULL)); |
659 | | | } |
660 | | | |
661 | | | // duplicate the first and last points, so that all points are drawn |
662 | | | // (C-R normally would draw from x[1] to x[n-1] |
663 | | | array_unshift($xarray, $xarray[0]); |
664 | | | array_unshift($yarray, $yarray[0]); |
665 | | | |
666 | | | $x=array_pop($xarray); |
667 | | | $y=array_pop($yarray); |
668 | | | array_push($xarray, $x); |
669 | | | array_push($xarray, $x); |
670 | | | array_push($yarray, $y); |
671 | | | array_push($yarray, $y); |
672 | | | |
673 | | | $npoints=count($xarray); |
674 | | | |
675 | | | $curvepoints=array |
676 | | | ( |
677 | | | ); |
678 | | | |
679 | | | // add in the very first point manually (the calc function skips this one to avoid duplicates, which mess up the distance stuff) |
680 | | | $curvepoints[]=array |
681 | | | ( |
682 | | | $xarray[0], |
683 | | | $yarray[0], |
684 | | | |
685 | | | ); |
686 | | | |
687 | | | $np=0; |
688 | | | $distance=0; |
689 | | | |
690 | | | for ($i=0; $i < ($npoints - 3); $i++) |
691 | | | { |
692 | | | list($newpoints, |
693 | | | $distance, |
694 | | | $np)=calculate_catmull_rom_span($np, $distance, $pointsperspan, $xarray[$i], |
695 | | | $yarray[$i], $xarray[$i + 1], $yarray[$i + 1], $xarray[$i + 2], |
696 | | | $yarray[$i + 2], $xarray[$i + 3], $yarray[$i + 3]); |
697 | | | $curvepoints=$curvepoints + $newpoints; |
698 | | | } |
699 | | | |
700 | | | return ($curvepoints); |
701 | | | } |
702 | | | |
703 | | | // Give a list of key points, calculate a "curve" through them |
704 | | | // return value is an array of triples (x,y,distance) |
705 | | | // this is here to mirror the real 'curve' version when we're using angled VIAs |
706 | | | // it means that all the stuff that expects an array of points with distances won't be upset. |
707 | | | function calc_straight(&$in_xarray, &$in_yarray,$pointsperspan = 12) |
708 | | | { |
709 | | | // search through the point list, for consecutive duplicate points |
710 | | | // (most common case will be a straight link with both NODEs at the same place, I think) |
711 | | | // strip those out, because they'll break the binary search/centre-point stuff |
712 | | | $last_x=NULL; |
713 | | | $last_y=NULL; |
714 | | | |
715 | | | for ($i=0; $i < count($in_xarray); $i++) |
716 | | | { |
717 | | | if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug |
718 | | | ("Dumping useless duplicate point on curve\n"); } |
719 | | | else |
720 | | | { |
721 | | | $xarray[]=$in_xarray[$i]; |
722 | | | $yarray[]=$in_yarray[$i]; |
723 | | | } |
724 | | | |
725 | | | $last_x=$in_xarray[$i]; |
726 | | | $last_y=$in_yarray[$i]; |
727 | | | } |
728 | | | |
729 | | | // only proceed if we still have at least two points! |
730 | | | if(count($xarray) <= 1) |
731 | | | { |
732 | | | warn ("Arrow not drawn, as it's 1-dimensional.\n"); |
733 | | | return (array(NULL, NULL, NULL, NULL)); |
734 | | | } |
735 | | | |
736 | | | $npoints=count($xarray); |
737 | | | |
738 | | | $curvepoints=array(); |
739 | | | |
740 | | | $np=0; |
741 | | | $distance=0; |
742 | | | |
743 | | | for ($i=0; $i < ($npoints -1); $i++) |
744 | | | { |
745 | | | // still subdivide the straight line, becuase other stuff makes assumptions about |
746 | | | // how often there is a point - at least find_distance_coords_angle breaks |
747 | | | $newdistance = sqrt( pow($xarray[$i+1] - $xarray[$i],2) + pow($yarray[$i+1] - $yarray[$i],2) ); |
748 | | | $dx = ($xarray[$i+1] - $xarray[$i])/$pointsperspan; |
749 | | | $dy = ($yarray[$i+1] - $yarray[$i])/$pointsperspan; |
750 | | | $dd = $newdistance/$pointsperspan; |
751 | | | |
752 | | | for($j=0; $j< $pointsperspan; $j++) |
753 | | | { |
754 | | | $x = $xarray[$i]+$j*$dx; |
755 | | | $y = $yarray[$i]+$j*$dy; |
756 | | | $d = $distance + $j*$dd; |
757 | | | |
758 | | | $curvepoints[] = array($x,$y,$d); |
759 | | | $np++; |
760 | | | } |
761 | | | $distance += $newdistance; |
762 | | | } |
763 | | | $curvepoints[] = array($xarray[$npoints-1],$yarray[$npoints-1],$distance); |
764 | | | |
765 | | | # print_r($curvepoints); |
766 | | | |
767 | | | return ($curvepoints); |
768 | | | } |
769 | | | |
770 | | | function calc_arrowsize($width,&$map,$linkname) |
771 | | | { |
772 | | | $arrowlengthfactor=4; |
773 | | | $arrowwidthfactor=2; |
774 | | | |
775 | | | // this is so I can use it in some test code - sorry! |
776 | | | if($map !== NULL) |
777 | | | { |
778 | | | if ($map->links[$linkname]->arrowstyle == 'compact') |
779 | | | { |
780 | | | $arrowlengthfactor=1; |
781 | | | $arrowwidthfactor=1; |
782 | | | } |
783 | | | |
784 | | | if (preg_match('/(\d+) (\d+)/', $map->links[$linkname]->arrowstyle, $matches)) |
785 | | | { |
786 | | | $arrowlengthfactor=$matches[1]; |
787 | | | $arrowwidthfactor=$matches[2]; |
788 | | | } |
789 | | | } |
790 | | | |
791 | | | $arrowsize = $width * $arrowlengthfactor; |
792 | | | $arrowwidth = $width * $arrowwidthfactor; |
793 | | | |
794 | | | return( array($arrowsize,$arrowwidth) ); |
795 | | | } |
796 | | | |
797 | | | function draw_straight($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map, |
798 | | | $q2_percent=50, $unidirectional=FALSE) |
799 | | | { |
800 | | | $totaldistance = $curvepoints[count($curvepoints)-1][DISTANCE]; |
801 | | | |
802 | | | if($unidirectional) |
803 | | | { |
804 | | | $halfway = $totaldistance; |
805 | | | $dirs = array(OUT); |
806 | | | $q2_percent = 100; |
807 | | | $halfway = $totaldistance * ($q2_percent/100); |
808 | | | list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); |
809 | | | |
810 | | | $spine[OUT] = $curvepoints; |
811 | | | } |
812 | | | else |
813 | | | { |
814 | | | // we'll split the spine in half here. |
815 | | | # $q2_percent = 50; |
816 | | | $halfway = $totaldistance * ($q2_percent/100); |
817 | | | |
818 | | | $dirs = array(OUT,IN); |
819 | | | # $dirs = array(IN); |
820 | | | |
821 | | | list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); |
822 | | | # print "Midpoint is: $totaldistance $halfway $halfwayindex $halfway_x,$halfway_y\n"; |
823 | | | |
824 | | | $spine[OUT] = array(); |
825 | | | $spine[IN] = array(); |
826 | | | $npoints = count($curvepoints)-1; |
827 | | | |
828 | | | for($i=0; $i<=$halfwayindex; $i++) |
829 | | | { |
830 | | | $spine[OUT] []= $curvepoints[$i]; |
831 | | | } |
832 | | | // finally, add the actual midpoint |
833 | | | $spine[OUT] []= array($halfway_x,$halfway_y, $halfway); |
834 | | | |
835 | | | // and then from the end to the middle for the other arrow |
836 | | | for($i=$npoints; $i>$halfwayindex; $i--) |
837 | | | { |
838 | | | // copy the original spine, but reversing the distance calculation |
839 | | | $spine[IN] []= array($curvepoints[$i][X], $curvepoints[$i][Y], $totaldistance - $curvepoints[$i][DISTANCE]); |
840 | | | } |
841 | | | // finally, add the actual midpoint |
842 | | | $spine[IN] []= array($halfway_x,$halfway_y, $totaldistance - $halfway); |
843 | | | } |
844 | | | |
845 | | | # wm_draw_marker_box($image,$map->selected, $halfway_x, $halfway_y ); |
846 | | | |
847 | | | // now we have two seperate spines, with distances, so that the arrowhead is the end of each. |
848 | | | // (or one, if it's unidir) |
849 | | | |
850 | | | // so we can loop along the spine for each one as a seperate entity |
851 | | | |
852 | | | // we calculate the arrow size up here, so that we can decide on the |
853 | | | // minimum length for a link. The arrowheads are the limiting factor. |
854 | | | list( $arrowsize[IN], $arrowwidth[IN] ) = calc_arrowsize( $widths[IN], $map, $linkname ); |
855 | | | list( $arrowsize[OUT], $arrowwidth[OUT] ) = calc_arrowsize( $widths[OUT], $map, $linkname ); |
856 | | | |
857 | | | // the 1.2 here is empirical. It ought to be 1 in theory. |
858 | | | // in practice, a link this short is useless anyway, especially with bwlabels. |
859 | | | $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]); |
860 | | | |
861 | | | foreach ($dirs as $dir) |
862 | | | { |
863 | | | # draw_spine($image, $spine[$dir],$map->selected); |
864 | | | #draw_spine_chain($image, $spine[$dir],$map->selected,3); |
865 | | | #print "=================\n$linkname/$dir\n"; |
866 | | | #dump_spine($spine[$dir]); |
867 | | | $n = count($spine[$dir]) - 1; |
868 | | | $l = $spine[$dir][$n][DISTANCE]; |
869 | | | |
870 | | | #print "L=$l N=$n\n"; |
871 | | | |
872 | | | // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos |
873 | | | $arrowsettings = array(+1, 0, $widths[$dir], 0, $fillcolours[$dir], $outlinecolour, 5); |
874 | | | |
875 | | | # print "Line is $n points to a distance of $l\n"; |
876 | | | if($l < $minimumlength) |
877 | | | { |
878 | | | warn("Skipping too-short line.\n"); |
879 | | | } |
880 | | | else |
881 | | | { |
882 | | | $arrow_d = $l - $arrowsize[$dir]; |
883 | | | # print "LENGTHS $l $arrow_d ".$arrowsize[$dir]."\n"; |
884 | | | list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($spine[$dir], $arrow_d); |
885 | | | # print "POS $pre_mid_x,$pre_mid_y $pre_midindex\n"; |
886 | | | $out = array_slice($spine[$dir], 0, $pre_midindex); |
887 | | | $out []= array($pre_mid_x, $pre_mid_y, $arrow_d); |
888 | | | |
889 | | | # wm_draw_marker_diamond($image, $map->selected, $pre_mid_x, $pre_mid_y, 5); |
890 | | | # imagearc($image,$pre_mid_x, $pre_mid_y ,15,15,0,360,$map->selected); |
891 | | | |
892 | | | # imagearc($image,$spine[$dir][$pre_midindex+1][X],$spine[$dir][$pre_midindex+1][Y],20,20,0,360,$map->selected); |
893 | | | # imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],20,20,0,360,$map->selected); |
894 | | | #imagearc($image,$pre_mid_x,$pre_mid_y,20,20,0,360,$map->selected); |
895 | | | #imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],12,12,0,360,$map->selected); |
896 | | | |
897 | | | $spine[$dir] = $out; |
898 | | | |
899 | | | $adx=($halfway_x - $pre_mid_x); |
900 | | | $ady=($halfway_y - $pre_mid_y); |
901 | | | $ll=sqrt(($adx * $adx) + ($ady * $ady)); |
902 | | | |
903 | | | $anx = $ady / $ll; |
904 | | | $any = -$adx / $ll; |
905 | | | |
906 | | | $ax1 = $pre_mid_x + $widths[$dir] * $anx; |
907 | | | $ay1 = $pre_mid_y + $widths[$dir] * $any; |
908 | | | |
909 | | | $ax2 = $pre_mid_x + $arrowwidth[$dir] * $anx; |
910 | | | $ay2 = $pre_mid_y + $arrowwidth[$dir] * $any; |
911 | | | |
912 | | | $ax3 = $halfway_x; |
913 | | | $ay3 = $halfway_y; |
914 | | | |
915 | | | $ax5 = $pre_mid_x - $widths[$dir] * $anx; |
916 | | | $ay5 = $pre_mid_y - $widths[$dir] * $any; |
917 | | | |
918 | | | $ax4 = $pre_mid_x - $arrowwidth[$dir] * $anx; |
919 | | | $ay4 = $pre_mid_y - $arrowwidth[$dir] * $any; |
920 | | | |
921 | | | # draw_spine($image,$spine[$dir],$map->selected); |
922 | | | |
923 | | | $simple = simplify_spine($spine[$dir]); |
924 | | | $newn = count($simple); |
925 | | | |
926 | | | # draw_spine($image,$simple,$map->selected); |
927 | | | |
928 | | | # print "Simplified to $newn points\n"; |
929 | | | # if($draw_skeleton) draw_spine_chain($im,$simple,$blue, 12); |
930 | | | # draw_spine_chain($image,$simple,$map->selected, 12); |
931 | | | # draw_spine_chain($image,$spine[$dir],$map->selected, 10); |
932 | | | |
933 | | | # draw_spine_chain($image,$simple,$map->selected, 12); |
934 | | | # draw_spine($image,$simple,$map->selected); |
935 | | | |
936 | | | // now do the actual drawing.... |
937 | | | |
938 | | | $numpoints=0; |
939 | | | $numrpoints=0; |
940 | | | |
941 | | | $finalpoints = array(); |
942 | | | $reversepoints = array(); |
943 | | | |
944 | | | $finalpoints[] = $simple[0][X]; |
945 | | | $finalpoints[] = $simple[0][Y]; |
946 | | | $numpoints++; |
947 | | | |
948 | | | $reversepoints[] = $simple[0][X]; |
949 | | | $reversepoints[] = $simple[0][Y]; |
950 | | | $numrpoints++; |
951 | | | |
952 | | | // before the main loop, add in the jump out to the corners |
953 | | | // if this is the first step, then we need to go from the middle to the outside edge first |
954 | | | // ( the loop may not run, but these corners are required) |
955 | | | $i = 0; |
956 | | | $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]); |
957 | | | $n1 = $v1->get_normal(); |
958 | | | |
959 | | | $finalpoints[] = $simple[$i][X] + $n1->dx*$widths[$dir]; |
960 | | | $finalpoints[] = $simple[$i][Y] + $n1->dy*$widths[$dir]; |
961 | | | $numpoints++; |
962 | | | |
963 | | | $reversepoints[] = $simple[$i][X] - $n1->dx*$widths[$dir]; |
964 | | | $reversepoints[] = $simple[$i][Y] - $n1->dy*$widths[$dir]; |
965 | | | $numrpoints++; |
966 | | | |
967 | | | $max_start = count($simple)-2; |
968 | | | # print "max_start is $max_start\n"; |
969 | | | for ($i=0; $i <$max_start; $i++) |
970 | | | { |
971 | | | $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]); |
972 | | | $v2 = new Vector($simple[$i+2][X] - $simple[$i+1][X], $simple[$i+2][Y] - $simple[$i+1][Y]); |
973 | | | $n1 = $v1->get_normal(); |
974 | | | $n2 = $v2->get_normal(); |
975 | | | |
976 | | | $capping = FALSE; |
977 | | | // figure out the angle between the lines - for very sharp turns, we should do something special |
978 | | | // (actually, their normals, but the angle is the same and we need the normals later) |
979 | | | $angle = rad2deg(atan2($n2->dy,$n2->dx) - atan2($n1->dy,$n1->dx)); |
980 | | | if($angle > 180) $angle -= 360; |
981 | | | if($angle < -180) $angle += 360; |
982 | | | |
983 | | | if(abs($angle)>169) |
984 | | | { |
985 | | | $capping = TRUE; |
986 | | | # print "Would cap. ($angle)\n"; |
987 | | | } |
988 | | | |
989 | | | // $capping = FALSE; // override that for now |
990 | | | // now figure out the geometry for where the next corners are |
991 | | | |
992 | | | list($xi1,$yi1) = line_crossing( $simple[$i][X] + $n1->dx * $widths[$dir], $simple[$i][Y] + $n1->dy * $widths[$dir], |
993 | | | $simple[$i+1][X] + $n1->dx * $widths[$dir], $simple[$i+1][Y] + $n1->dy * $widths[$dir], |
994 | | | $simple[$i+1][X] + $n2->dx * $widths[$dir], $simple[$i+1][Y] + $n2->dy * $widths[$dir], |
995 | | | $simple[$i+2][X] + $n2->dx * $widths[$dir], $simple[$i+2][Y] + $n2->dy * $widths[$dir] |
996 | | | ); |
997 | | | |
998 | | | list($xi2,$yi2) = line_crossing( $simple[$i][X] - $n1->dx * $widths[$dir], $simple[$i][Y] - $n1->dy * $widths[$dir], |
999 | | | $simple[$i+1][X] - $n1->dx * $widths[$dir], $simple[$i+1][Y] - $n1->dy * $widths[$dir], |
1000 | | | $simple[$i+1][X] - $n2->dx * $widths[$dir], $simple[$i+1][Y] - $n2->dy * $widths[$dir], |
1001 | | | $simple[$i+2][X] - $n2->dx * $widths[$dir], $simple[$i+2][Y] - $n2->dy * $widths[$dir] |
1002 | | | ); |
1003 | | | |
1004 | | | if(!$capping) |
1005 | | | { |
1006 | | | $finalpoints[] = $xi1; |
1007 | | | $finalpoints[] = $yi1; |
1008 | | | $numpoints++; |
1009 | | | |
1010 | | | $reversepoints[] = $xi2; |
1011 | | | $reversepoints[] = $yi2; |
1012 | | | $numrpoints++; |
1013 | | | } |
1014 | | | else |
1015 | | | { |
1016 | | | // in here, we need to decide which is the 'outside' of the corner, |
1017 | | | // because that's what we flatten. The inside of the corner is left alone. |
1018 | | | // - depending on the relative angle between the two segments, it could |
1019 | | | // be either one of these points. |
1020 | | | |
1021 | | | list($xi3,$yi3) = line_crossing( $simple[$i][X] + $n1->dx*$widths[$dir], $simple[$i][Y] + $n1->dy*$widths[$dir], |
1022 | | | $simple[$i+1][X] + $n1->dx*$widths[$dir], $simple[$i+1][Y] + $n1->dy*$widths[$dir], |
1023 | | | $simple[$i+1][X] - $n2->dx*$widths[$dir], $simple[$i+1][Y] - $n2->dy*$widths[$dir], |
1024 | | | $simple[$i+2][X] - $n2->dx*$widths[$dir], $simple[$i+2][Y] - $n2->dy*$widths[$dir] |
1025 | | | ); |
1026 | | | |
1027 | | | list($xi4,$yi4) = line_crossing( $simple[$i][X] - $n1->dx*$widths[$dir], $simple[$i][Y] - $n1->dy*$widths[$dir], |
1028 | | | $simple[$i+1][X] - $n1->dx*$widths[$dir], $simple[$i+1][Y] - $n1->dy*$widths[$dir], |
1029 | | | $simple[$i+1][X] + $n2->dx*$widths[$dir], $simple[$i+1][Y] + $n2->dy*$widths[$dir], |
1030 | | | $simple[$i+2][X] + $n2->dx*$widths[$dir], $simple[$i+2][Y] + $n2->dy*$widths[$dir] |
1031 | | | ); |
1032 | | | if($angle < 0) |
1033 | | | { |
1034 | | | $finalpoints[] = $xi3; |
1035 | | | $finalpoints[] = $yi3; |
1036 | | | $numpoints++; |
1037 | | | |
1038 | | | $finalpoints[] = $xi4; |
1039 | | | $finalpoints[] = $yi4; |
1040 | | | $numpoints++; |
1041 | | | |
1042 | | | $reversepoints[] = $xi2; |
1043 | | | $reversepoints[] = $yi2; |
1044 | | | $numrpoints++; |
1045 | | | } |
1046 | | | else |
1047 | | | { |
1048 | | | $reversepoints[] = $xi4; |
1049 | | | $reversepoints[] = $yi4; |
1050 | | | $numrpoints++; |
1051 | | | |
1052 | | | $reversepoints[] = $xi3; |
1053 | | | $reversepoints[] = $yi3; |
1054 | | | $numrpoints++; |
1055 | | | |
1056 | | | $finalpoints[] = $xi1; |
1057 | | | $finalpoints[] = $yi1; |
1058 | | | $numpoints++; |
1059 | | | } |
1060 | | | |
1061 | | | } |
1062 | | | } |
1063 | | | |
1064 | | | // at this end, we add the arrowhead |
1065 | | | |
1066 | | | $finalpoints[] = $ax1; |
1067 | | | $finalpoints[] = $ay1; |
1068 | | | $finalpoints[] = $ax2; |
1069 | | | $finalpoints[] = $ay2; |
1070 | | | $finalpoints[] = $ax3; |
1071 | | | $finalpoints[] = $ay3; |
1072 | | | $finalpoints[] = $ax4; |
1073 | | | $finalpoints[] = $ay4; |
1074 | | | $finalpoints[] = $ax5; |
1075 | | | $finalpoints[] = $ay5; |
1076 | | | |
1077 | | | $numpoints += 5; |
1078 | | | |
1079 | | | // combine the forwards and backwards paths, to make a complete loop |
1080 | | | for($i=($numrpoints-1)*2; $i>=0; $i-=2) |
1081 | | | { |
1082 | | | $x = $reversepoints[$i]; |
1083 | | | $y = $reversepoints[$i+1]; |
1084 | | | |
1085 | | | $finalpoints[] = $x; |
1086 | | | $finalpoints[] = $y; |
1087 | | | $numpoints++; |
1088 | | | } |
1089 | | | // $finalpoints[] contains a complete outline of the line at this stage |
1090 | | | |
1091 | | | if (!is_null($fillcolours[$dir])) |
1092 | | | { |
1093 | | | wimagefilledpolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[4]); |
1094 | | | } |
1095 | | | else |
1096 | | | { |
1097 | | | debug("Not drawing $linkname ($dir) fill because there is no fill colour\n"); |
1098 | | | } |
1099 | | | |
1100 | | | $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir"; |
1101 | | | $map->imap->addArea("Polygon", $areaname, '', $finalpoints); |
1102 | | | debug ("Adding Poly imagemap for $areaname\n"); |
1103 | | | |
1104 | | | if (!is_null($outlinecolour)) |
1105 | | | { |
1106 | | | wimagepolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[5]); |
1107 | | | } |
1108 | | | else |
1109 | | | { |
1110 | | | debug("Not drawing $linkname ($dir) outline because there is no outline colour\n"); |
1111 | | | } |
1112 | | | } |
1113 | | | } |
1114 | | | } |
1115 | | | |
1116 | | | // top-level function that takes a two lists to define some points, and draws a weathermap link |
1117 | | | // - this takes care of all the extras, like arrowheads, and where to put the bandwidth labels |
1118 | | | // curvepoints is an array of the points the curve passes through |
1119 | | | // width is the link width (the actual width is twice this) |
1120 | | | // outlinecolour is a GD colour reference |
1121 | | | // fillcolours is an array of two more colour references, one for the out, and one for the in spans |
1122 | | | function draw_curve($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map, |
1123 | | | $q2_percent=50, $unidirectional=FALSE) |
1124 | | | { |
1125 | | | // now we have a 'spine' - all the central points for this curve. |
1126 | | | // time to flesh it out to the right width, and figure out where to draw arrows and bandwidth boxes... |
1127 | | | |
1128 | | | // get the full length of the curve from the last point |
1129 | | | $totaldistance = $curvepoints[count($curvepoints)-1][2]; |
1130 | | | // find where the in and out arrows will join (normally halfway point) |
1131 | | | $halfway = $totaldistance * ($q2_percent/100); |
1132 | | | |
1133 | | | $dirs = array(OUT,IN); |
1134 | | | |
1135 | | | // for a unidirectional map, we just ignore the second half (direction = -1) |
1136 | | | if($unidirectional) |
1137 | | | { |
1138 | | | $halfway = $totaldistance; |
1139 | | | $dirs = array(OUT); |
1140 | | | } |
1141 | | | |
1142 | | | // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos |
1143 | | | $arrowsettings[OUT] = array(+1, 0, $widths[OUT], 0, $fillcolours[OUT], $outlinecolour, 5); |
1144 | | | $arrowsettings[IN] = array(-1, count($curvepoints) - 1, $widths[IN], 0, $fillcolours[IN], $outlinecolour, 95); |
1145 | | | |
1146 | | | // we calculate the arrow size up here, so that we can decide on the |
1147 | | | // minimum length for a link. The arrowheads are the limiting factor. |
1148 | | | list($arrowsize[IN],$arrowwidth[IN]) = calc_arrowsize($widths[IN], $map, $linkname); |
1149 | | | list($arrowsize[OUT],$arrowwidth[OUT]) = calc_arrowsize($widths[OUT], $map, $linkname); |
1150 | | | |
1151 | | | // the 1.2 here is empirical. It ought to be 1 in theory. |
1152 | | | // in practice, a link this short is useless anyway, especially with bwlabels. |
1153 | | | $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]); |
1154 | | | |
1155 | | | # warn("$linkname: Total: $totaldistance $arrowsize $arrowwidth $minimumlength\n"); |
1156 | | | if($totaldistance <= $minimumlength) |
1157 | | | { |
1158 | | | warn("Skipping drawing very short link ($linkname). Impossible to draw! Try changing WIDTH or ARROWSTYLE? [WMWARN01]\n"); |
1159 | | | return; |
1160 | | | } |
1161 | | | |
1162 | | | |
1163 | | | list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway); |
1164 | | | |
1165 | | | // loop over direction here |
1166 | | | // direction is 1.0 for the first half (forwards through the pointlist), and -1.0 for the second half (backwards from the end) |
1167 | | | // - used as a multiplier on anything that looks forwards or backwards through the list |
1168 | | | |
1169 | | | foreach ($dirs as $dir) |
1170 | | | { |
1171 | | | $direction = $arrowsettings[$dir][0]; |
1172 | | | // $width = $widths[$dir]; |
1173 | | | // this is the last index before the arrowhead starts |
1174 | | | list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($curvepoints,$halfway - $direction * $arrowsize[$dir]); |
1175 | | | |
1176 | | | $there_points=array(); |
1177 | | | $back_points=array(); |
1178 | | | $arrowpoints=array(); |
1179 | | | |
1180 | | | # if ($direction < 0) { $start=count($curvepoints) - 1; } |
1181 | | | # else { $start=0; } |
1182 | | | $start = $arrowsettings[$dir][1]; |
1183 | | | |
1184 | | | for ($i=$start; $i != $pre_midindex; $i+=$direction) |
1185 | | | { |
1186 | | | // for each point on the spine, produce two points normal to it's direction, |
1187 | | | // each is $width away from the spine, but we build up the two lists in the opposite order, |
1188 | | | // so that when they are joined together, we get one continuous line |
1189 | | | |
1190 | | | $dx=$curvepoints[$i + $direction][0] - $curvepoints[$i][0]; |
1191 | | | $dy=$curvepoints[$i + $direction][1] - $curvepoints[$i][1]; |
1192 | | | $l=sqrt(($dx * $dx) + ($dy * $dy)); |
1193 | | | $nx=$dy / $l; |
1194 | | | $ny=-$dx / $l; |
1195 | | | |
1196 | | | $there_points[]=$curvepoints[$i][0] + $direction * $widths[$dir] * $nx; |
1197 | | | $there_points[]=$curvepoints[$i][1] + $direction * $widths[$dir] * $ny; |
1198 | | | |
1199 | | | $back_points[]=$curvepoints[$i][0] - $direction * $widths[$dir] * $nx; |
1200 | | | $back_points[]=$curvepoints[$i][1] - $direction * $widths[$dir] * $ny; |
1201 | | | } |
1202 | | | |
1203 | | | // all the normal line is done, now lets add an arrowhead on |
1204 | | | |
1205 | | | $adx=($halfway_x - $pre_mid_x); |
1206 | | | $ady=($halfway_y - $pre_mid_y); |
1207 | | | $l=sqrt(($adx * $adx) + ($ady * $ady)); |
1208 | | | |
1209 | | | $anx=$ady / $l; |
1210 | | | $any=-$adx / $l; |
1211 | | | |
1212 | | | $there_points[]=$pre_mid_x + $direction * $widths[$dir] * $anx; |
1213 | | | $there_points[]=$pre_mid_y + $direction * $widths[$dir] * $any; |
1214 | | | |
1215 | | | $there_points[]=$pre_mid_x + $direction * $arrowwidth[$dir] * $anx; |
1216 | | | $there_points[]=$pre_mid_y + $direction * $arrowwidth[$dir] * $any; |
1217 | | | |
1218 | | | $there_points[]=$halfway_x; |
1219 | | | $there_points[]=$halfway_y; |
1220 | | | |
1221 | | | $there_points[]=$pre_mid_x - $direction * $arrowwidth[$dir] * $anx; |
1222 | | | $there_points[]=$pre_mid_y - $direction * $arrowwidth[$dir] * $any; |
1223 | | | |
1224 | | | $there_points[]=$pre_mid_x - $direction * $widths[$dir] * $anx; |
1225 | | | $there_points[]=$pre_mid_y - $direction * $widths[$dir] * $any; |
1226 | | | |
1227 | | | // all points done, now combine the lists, and produce the final result. |
1228 | | | $metapts = ""; |
1229 | | | $y=array_pop($back_points); |
1230 | | | $x=array_pop($back_points); |
1231 | | | do |
1232 | | | { |
1233 | | | $metapts .= " $x $y"; |
1234 | | | $there_points[]=$x; |
1235 | | | $there_points[]=$y; |
1236 | | | $y=array_pop($back_points); |
1237 | | | $x=array_pop($back_points); |
1238 | | | } while (!is_null($y)); |
1239 | | | |
1240 | | | $arrayindex=1; |
1241 | | | |
1242 | | | if ($direction < 0) $arrayindex=0; |
1243 | | | |
1244 | | | if (!is_null($fillcolours[$arrayindex])) |
1245 | | | { |
1246 | | | wimagefilledpolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][4]); |
1247 | | | } |
1248 | | | else |
1249 | | | { |
1250 | | | debug("Not drawing $linkname ($dir) fill because there is no fill colour\n"); |
1251 | | | } |
1252 | | | |
1253 | | | # $areaname = "LINK:" . $linkname. ":$dir"; |
1254 | | | $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir"; |
1255 | | | $map->imap->addArea("Polygon", $areaname, '', $there_points); |
1256 | | | debug ("Adding Poly imagemap for $areaname\n"); |
1257 | | | |
1258 | | | if (!is_null($outlinecolour)) |
1259 | | | { |
1260 | | | wimagepolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][5]); |
1261 | | | } |
1262 | | | else |
1263 | | | { |
1264 | | | debug("Not drawing $linkname ($dir) outline because there is no outline colour\n"); |
1265 | | | } |
1266 | | | } |
1267 | | | } |
1268 | | | |
1269 | | | // Take a spine, and strip out all the points that are co-linear with the points either side of them |
1270 | | | function simplify_spine(&$input, $epsilon=1e-10) |
1271 | | | { |
1272 | | | $output = array(); |
1273 | | | |
1274 | | | $output []= $input[0]; |
1275 | | | $n=1; |
1276 | | | $c = count($input)-2; |
1277 | | | $skip=0; |
1278 | | | |
1279 | | | for($n=1; $n<=$c; $n++) |
1280 | | | { |
1281 | | | $x = $input[$n][X]; |
1282 | | | $y = $input[$n][Y]; |
1283 | | | |
1284 | | | // figure out the area of the triangle formed by this point, and the one before and after |
1285 | | | $a = abs($input[$n-1][X] * ( $input[$n][Y] - $input[$n+1][Y] ) |
1286 | | | + $input[$n][X] * ( $input[$n+1][Y] - $input[$n-1][Y] ) |
1287 | | | + $input[$n+1][X] * ( $input[$n-1][Y] - $input[$n][Y] ) ); |
1288 | | | |
1289 | | | # print "$n $x,$y $a"; |
1290 | | | |
1291 | | | if ( $a > $epsilon) |
1292 | | | // if(1==1) |
1293 | | | { |
1294 | | | $output []= $input[$n]; |
1295 | | | # print " KEEP"; |
1296 | | | } |
1297 | | | else |
1298 | | | { |
1299 | | | // ignore n |
1300 | | | $skip++; |
1301 | | | # print " SKIP"; |
1302 | | | |
1303 | | | } |
1304 | | | # print "\n"; |
1305 | | | } |
1306 | | | |
1307 | | | debug("Skipped $skip points of $c\n"); |
1308 | | | |
1309 | | | # print "------------------------\n"; |
1310 | | | |
1311 | | | $output []= $input[$c+1]; |
1312 | | | return $output; |
1313 | | | } |
1314 | | | |
1315 | | | function unformat_number($instring, $kilo = 1000) |
1316 | | | { |
1317 | | | $matches=0; |
1318 | | | $number=0; |
1319 | | | |
1320 | | | if (preg_match("/([0-9\.]+)(M|G|K|T|m|u)/", $instring, $matches)) |
1321 | | | { |
1322 | | | $number=floatval($matches[1]); |
1323 | | | |
1324 | | | if ($matches[2] == 'K') { $number=$number * $kilo; } |
1325 | | | if ($matches[2] == 'M') { $number=$number * $kilo * $kilo; } |
1326 | | | if ($matches[2] == 'G') { $number=$number * $kilo * $kilo * $kilo; } |
1327 | | | if ($matches[2] == 'T') { $number=$number * $kilo * $kilo * $kilo * $kilo; } |
1328 | | | // new, for absolute datastyle. Think seconds. |
1329 | | | if ($matches[2] == 'm') { $number=$number / $kilo; } |
1330 | | | if ($matches[2] == 'u') { $number=$number / ($kilo * $kilo); } |
1331 | | | } |
1332 | | | else { $number=floatval($instring); } |
1333 | | | |
1334 | | | return ($number); |
1335 | | | } |
1336 | | | |
1337 | | | // given a compass-point, and a width & height, return a tuple of the x,y offsets |
1338 | | | function calc_offset($offsetstring, $width, $height) |
1339 | | | { |
1340 | | | if(preg_match("/^([-+]?\d+):([-+]?\d+)$/",$offsetstring,$matches)) |
1341 | | | { |
1342 | | | debug("Numeric Offset found\n"); |
1343 | | | return(array($matches[1],$matches[2])); |
1344 | | | } |
1345 | | | elseif(preg_match("/(NE|SE|NW|SW|N|S|E|W|C)(\d+)?$/i",$offsetstring,$matches)) |
1346 | | | { |
1347 | | | $multiply = 1; |
1348 | | | if( isset($matches[2] ) ) |
1349 | | | { |
1350 | | | $multiply = intval($matches[2])/100; |
1351 | | | debug("Percentage compass offset: multiply by $multiply"); |
1352 | | | } |
1353 | | | |
1354 | | | $height = $height * $multiply; |
1355 | | | $width = $width * $multiply; |
1356 | | | |
1357 | | | switch (strtoupper($matches[1])) |
1358 | | | { |
1359 | | | case 'N': |
1360 | | | return (array(0, -$height / 2)); |
1361 | | | |
1362 | | | break; |
1363 | | | |
1364 | | | case 'S': |
1365 | | | return (array(0, $height / 2)); |
1366 | | | |
1367 | | | break; |
1368 | | | |
1369 | | | case 'E': |
1370 | | | return (array(+$width / 2, 0)); |
1371 | | | |
1372 | | | break; |
1373 | | | |
1374 | | | case 'W': |
1375 | | | return (array(-$width / 2, 0)); |
1376 | | | |
1377 | | | break; |
1378 | | | |
1379 | | | case 'NW': |
1380 | | | return (array(-$width / 2, -$height / 2)); |
1381 | | | |
1382 | | | break; |
1383 | | | |
1384 | | | case 'NE': |
1385 | | | return (array($width / 2, -$height / 2)); |
1386 | | | |
1387 | | | break; |
1388 | | | |
1389 | | | case 'SW': |
1390 | | | return (array(-$width / 2, $height / 2)); |
1391 | | | |
1392 | | | break; |
1393 | | | |
1394 | | | case 'SE': |
1395 | | | return (array($width / 2, $height / 2)); |
1396 | | | |
1397 | | | break; |
1398 | | | |
1399 | | | case 'C': |
1400 | | | default: |
1401 | | | return (array(0, 0)); |
1402 | | | |
1403 | | | break; |
1404 | | | } |
1405 | | | } |
1406 | | | elseif( preg_match("/(-?\d+)r(\d+)$/i",$offsetstring,$matches) ) |
1407 | | | { |
1408 | | | $angle = intval($matches[1]); |
1409 | | | $distance = intval($matches[2]); |
1410 | | | |
1411 | | | $x = $distance * sin(deg2rad($angle)); |
1412 | | | $y = - $distance * cos(deg2rad($angle)); |
1413 | | | |
1414 | | | return (array($x,$y)); |
1415 | | | |
1416 | | | } |
1417 | | | else |
1418 | | | { |
1419 | | | warn("Got a position offset that didn't make sense ($offsetstring)."); |
1420 | | | return (array(0, 0)); |
1421 | | | } |
1422 | | | |
1423 | | | |
1424 | | | } |
1425 | | | |
1426 | | | // These next two are based on perl's Number::Format module |
1427 | | | // by William R. Ward, chopped down to just what I needed |
1428 | | | |
1429 | | | function format_number($number, $precision = 2, $trailing_zeroes = 0) |
1430 | | | { |
1431 | | | $sign=1; |
1432 | | | |
1433 | | | if ($number < 0) |
1434 | | | { |
1435 | | | $number=abs($number); |
1436 | | | $sign=-1; |
1437 | | | } |
1438 | | | |
1439 | | | $number=round($number, $precision); |
1440 | | | $integer=intval($number); |
1441 | | | |
1442 | | | if (strlen($integer) < strlen($number)) { $decimal=substr($number, strlen($integer) + 1); } |
1443 | | | |
1444 | | | if (!isset($decimal)) { $decimal=''; } |
1445 | | | |
1446 | | | $integer=$sign * $integer; |
1447 | | | |
1448 | | | if ($decimal == '') { return ($integer); } |
1449 | | | else { return ($integer . "." . $decimal); } |
1450 | | | } |
1451 | | | |
1452 | | | function nice_bandwidth($number, $kilo = 1000,$decimals=1,$below_one=TRUE) |
1453 | | | { |
1454 | | | $suffix=''; |
1455 | | | |
1456 | | | if ($number == 0) |
1457 | | | return '0'; |
1458 | | | |
1459 | | | $mega=$kilo * $kilo; |
1460 | | | $giga=$mega * $kilo; |
1461 | | | $tera=$giga * $kilo; |
1462 | | | |
1463 | | | $milli = 1/$kilo; |
1464 | | | $micro = 1/$mega; |
1465 | | | $nano = 1/$giga; |
1466 | | | |
1467 | | | if ($number >= $tera) |
1468 | | | { |
1469 | | | $number/=$tera; |
1470 | | | $suffix="T"; |
1471 | | | } |
1472 | | | elseif ($number >= $giga) |
1473 | | | { |
1474 | | | $number/=$giga; |
1475 | | | $suffix="G"; |
1476 | | | } |
1477 | | | elseif ($number >= $mega) |
1478 | | | { |
1479 | | | $number/=$mega; |
1480 | | | $suffix="M"; |
1481 | | | } |
1482 | | | elseif ($number >= $kilo) |
1483 | | | { |
1484 | | | $number/=$kilo; |
1485 | | | $suffix="K"; |
1486 | | | } |
1487 | | | elseif ($number >= 1) |
1488 | | | { |
1489 | | | $number = $number; |
1490 | | | $suffix=""; |
1491 | | | } |
1492 | | | elseif (($below_one==TRUE) && ($number >= $milli)) |
1493 | | | { |
1494 | | | $number/=$milli; |
1495 | | | $suffix="m"; |
1496 | | | } |
1497 | | | elseif (($below_one==TRUE) && ($number >= $micro)) |
1498 | | | { |
1499 | | | $number/=$micro; |
1500 | | | $suffix="u"; |
1501 | | | } |
1502 | | | elseif (($below_one==TRUE) && ($number >= $nano)) |
1503 | | | { |
1504 | | | $number/=$nano; |
1505 | | | $suffix="n"; |
1506 | | | } |
1507 | | | |
1508 | | | $result=format_number($number, $decimals) . $suffix; |
1509 | | | return ($result); |
1510 | | | } |
1511 | | | |
1512 | | | function nice_scalar($number, $kilo = 1000, $decimals=1) |
1513 | | | { |
1514 | | | $suffix = ''; |
1515 | | | $prefix = ''; |
1516 | | | |
1517 | | | if ($number == 0) |
1518 | | | return '0'; |
1519 | | | |
1520 | | | if($number < 0) |
1521 | | | { |
1522 | | | $number = -$number; |
1523 | | | $prefix = '-'; |
1524 | | | } |
1525 | | | |
1526 | | | $mega=$kilo * $kilo; |
1527 | | | $giga=$mega * $kilo; |
1528 | | | $tera=$giga * $kilo; |
1529 | | | |
1530 | | | if ($number > $tera) |
1531 | | | { |
1532 | | | $number/=$tera; |
1533 | | | $suffix="T"; |
1534 | | | } |
1535 | | | elseif ($number > $giga) |
1536 | | | { |
1537 | | | $number/=$giga; |
1538 | | | $suffix="G"; |
1539 | | | } |
1540 | | | elseif ($number > $mega) |
1541 | | | { |
1542 | | | $number/=$mega; |
1543 | | | $suffix="M"; |
1544 | | | } |
1545 | | | elseif ($number > $kilo) |
1546 | | | { |
1547 | | | $number/=$kilo; |
1548 | | | $suffix="K"; |
1549 | | | } |
1550 | | | elseif ($number > 1) |
1551 | | | { |
1552 | | | $number = $number; |
1553 | | | $suffix=""; |
1554 | | | } |
1555 | | | elseif ($number < (1 / ($kilo))) |
1556 | | | { |
1557 | | | $number=$number * $mega; |
1558 | | | $suffix="u"; |
1559 | | | } |
1560 | | | elseif ($number < 1) |
1561 | | | { |
1562 | | | $number=$number * $kilo; |
1563 | | | $suffix="m"; |
1564 | | | } |
1565 | | | |
1566 | | | $result = $prefix . format_number($number, $decimals) . $suffix; |
1567 | | | return ($result); |
1568 | | | } |
1569 | | | |
1570 | | | |
1571 | | | // *********************************************** |
1572 | | | |
1573 | | | // we use enough points in various places to make it worth a small class to save some variable-pairs. |
1574 | | | class Point |
1575 | | | { |
1576 | | | var $x, $y; |
1577 | | | |
1578 | | | function Point($x=0,$y=0) |
1579 | | | { |
1580 | | | $this->x = $x; |
1581 | | | $this->y = $y; |
1582 | | | } |
1583 | | | } |
1584 | | | |
1585 | | | // similarly for 2D vectors |
1586 | | | class Vector |
1587 | | | { |
1588 | | | var $dx, $dy; |
1589 | | | |
1590 | | | function Vector($dx=0,$dy=0) |
1591 | | | { |
1592 | | | $this->dx = $dx; |
1593 | | | $this->dy = $dy; |
1594 | | | } |
1595 | | | |
1596 | | | function get_normal() |
1597 | | | { |
1598 | | | $len = $this->length(); |
1599 | | | |
1600 | | | $nx1 = $this->dy / $len; |
1601 | | | $ny1 = -$this->dx / $len; |
1602 | | | |
1603 | | | return( new Vector($nx1, $ny1)); |
1604 | | | } |
1605 | | | |
1606 | | | function normalise() |
1607 | | | { |
1608 | | | $len = $this->length(); |
1609 | | | $this->dx = $this->dx/$len; |
1610 | | | $this->dy = $this->dy/$len; |
1611 | | | } |
1612 | | | |
1613 | | | function length() |
1614 | | | { |
1615 | | | return( sqrt(($this->dx)*($this->dx) + ($this->dy)*($this->dy)) ); |
1616 | | | } |
1617 | | | } |
1618 | | | |
1619 | | | class Colour |
1620 | | | { |
1621 | | | var $r,$g,$b, $alpha; |
1622 | | | |
1623 | | | |
1624 | | | // take in an existing value and create a Colour object for it |
1625 | | | function Colour() |
1626 | | | { |
1627 | | | if(func_num_args() == 3) # a set of 3 colours |
1628 | | | { |
1629 | | | $this->r = func_get_arg(0); # r |
1630 | | | $this->g = func_get_arg(1); # g |
1631 | | | $this->b = func_get_arg(2); # b |
1632 | | | #print "3 args"; |
1633 | | | #print $this->as_string()."--"; |
1634 | | | } |
1635 | | | |
1636 | | | if( (func_num_args() == 1) && gettype(func_get_arg(0))=='array' ) # an array of 3 colours |
1637 | | | { |
1638 | | | #print "1 args"; |
1639 | | | $ary = func_get_arg(0); |
1640 | | | $this->r = $ary[0]; |
1641 | | | $this->g = $ary[1]; |
1642 | | | $this->b = $ary[2]; |
1643 | | | } |
1644 | | | } |
1645 | | | |
1646 | | | // Is this a transparent/none colour? |
1647 | | | function is_real() |
1648 | | | { |
1649 | | | if($this->r >= 0 && $this->g >=0 && $this->b >= 0) |
1650 | | | { |
1651 | | | return true; |
1652 | | | } |
1653 | | | else |
1654 | | | { |
1655 | | | return false; |
1656 | | | } |
1657 | | | } |
1658 | | | |
1659 | | | // Is this a transparent/none colour? |
1660 | | | function is_none() |
1661 | | | { |
1662 | | | if($this->r == -1 && $this->g == -1 && $this->b == -1) |
1663 | | | { |
1664 | | | return true; |
1665 | | | } |
1666 | | | else |
1667 | | | { |
1668 | | | return false; |
1669 | | | } |
1670 | | | } |
1671 | | | |
1672 | | | // Is this a contrast colour? |
1673 | | | function is_contrast() |
1674 | | | { |
1675 | | | if($this->r == -3 && $this->g == -3 && $this->b == -3) |
1676 | | | { |
1677 | | | return true; |
1678 | | | } |
1679 | | | else |
1680 | | | { |
1681 | | | return false; |
1682 | | | } |
1683 | | | } |
1684 | | | |
1685 | | | // Is this a copy colour? |
1686 | | | function is_copy() |
1687 | | | { |
1688 | | | if($this->r == -2 && $this->g == -2 && $this->b == -2) |
1689 | | | { |
1690 | | | return true; |
1691 | | | } |
1692 | | | else |
1693 | | | { |
1694 | | | return false; |
1695 | | | } |
1696 | | | } |
1697 | | | |
1698 | | | // allocate a colour in the appropriate image context |
1699 | | | // - things like scale colours are used in multiple images now (the scale, several nodes, the main map...) |
1700 | | | function gdallocate($image_ref) |
1701 | | | { |
1702 | | | if($this->is_none()) |
1703 | | | { |
1704 | | | return NULL; |
1705 | | | } |
1706 | | | else |
1707 | | | { |
1708 | | | return(myimagecolorallocate($image_ref, $this->r, $this->g, $this->b)); |
1709 | | | } |
1710 | | | } |
1711 | | | |
1712 | | | // based on an idea from: http://www.bennadel.com/index.cfm?dax=blog:902.view |
1713 | | | function contrast_ary() |
1714 | | | { |
1715 | | | if( (($this->r + $this->g + $this->b) > 500) |
1716 | | | || ($this->g > 140) |
1717 | | | ) |
1718 | | | { |
1719 | | | return( array(0,0,0) ); |
1720 | | | } |
1721 | | | else |
1722 | | | { |
1723 | | | return( array(255,255,255) ); |
1724 | | | } |
1725 | | | } |
1726 | | | |
1727 | | | function contrast() |
1728 | | | { |
1729 | | | return( new Colour($this->contrast_ary() ) ); |
1730 | | | } |
1731 | | | |
1732 | | | // make a printable version, for debugging |
1733 | | | // - optionally take a format string, so we can use it for other things (like WriteConfig, or hex in stylesheets) |
1734 | | | function as_string($format = "RGB(%d,%d,%d)") |
1735 | | | { |
1736 | | | return (sprintf($format, $this->r, $this->g, $this->b)); |
1737 | | | } |
1738 | | | |
1739 | | | function as_config() |
1740 | | | { |
1741 | | | return $this->as_string("%d %d %d"); |
1742 | | | } |
1743 | | | |
1744 | | | function as_html() |
1745 | | | { |
1746 | | | if($this->is_real()) |
1747 | | | { |
1748 | | | return $this->as_string("#%02x%02x%02x"); |
1749 | | | } |
1750 | | | else |
1751 | | | { |
1752 | | | return ""; |
1753 | | | } |
1754 | | | } |
1755 | | | } |
1756 | | | |
1757 | | | // A series of wrapper functions around all the GD function calls |
1758 | | | // - I added these in so I could make a 'metafile' easily of all the |
1759 | | | // drawing commands for a map. I have a basic Perl-Cairo script that makes |
1760 | | | // anti-aliased maps from these, using Cairo instead of GD. |
1761 | | | |
1762 | | | function metadump($string, $truncate=FALSE) |
1763 | | | { |
1764 | | | // comment this line to get a metafile for this map |
1765 | | | return; |
1766 | | | |
1767 | | | if($truncate) |
1768 | | | { |
1769 | | | $fd = fopen("metadump.txt","w+"); |
1770 | | | } |
1771 | | | else |
1772 | | | { |
1773 | | | $fd = fopen("metadump.txt","a"); |
1774 | | | } |
1775 | | | fputs($fd,$string."\n"); |
1776 | | | fclose($fd); |
1777 | | | } |
1778 | | | |
1779 | | | function metacolour(&$col) |
1780 | | | { |
1781 | | | return ($col['red1']." ".$col['green1']." ".$col['blue1']); |
1782 | | | } |
1783 | | | |
1784 | | | function wimagecreate($width,$height) |
1785 | | | { |
1786 | | | metadump("NEWIMAGE $width $height"); |
1787 | | | return(imagecreate($width,$height)); |
1788 | | | } |
1789 | | | |
1790 | | | function wimagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color ) |
1791 | | | { |
1792 | | | if ($color===NULL) return; |
1793 | | | |
1794 | | | $col = imagecolorsforindex($image, $color); |
1795 | | | $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; |
1796 | | | $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; |
1797 | | | |
1798 | | | metadump("FRECT $x1 $y1 $x2 $y2 $r $g $b $a"); |
1799 | | | return(imagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color )); |
1800 | | | } |
1801 | | | |
1802 | | | function wimagerectangle( $image ,$x1, $y1, $x2, $y2, $color ) |
1803 | | | { |
1804 | | | if ($color===NULL) return; |
1805 | | | |
1806 | | | $col = imagecolorsforindex($image, $color); |
1807 | | | $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; |
1808 | | | $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; |
1809 | | | |
1810 | | | metadump("RECT $x1 $y1 $x2 $y2 $r $g $b $a"); |
1811 | | | return(imagerectangle( $image ,$x1, $y1, $x2, $y2, $color )); |
1812 | | | } |
1813 | | | |
1814 | | | function wimagepolygon($image, $points, $num_points, $color) |
1815 | | | { |
1816 | | | if ($color===NULL) return; |
1817 | | | |
1818 | | | $col = imagecolorsforindex($image, $color); |
1819 | | | $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; |
1820 | | | $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; |
1821 | | | |
1822 | | | $pts = ""; |
1823 | | | for ($i=0; $i < $num_points; $i++) |
1824 | | | { |
1825 | | | $pts .= $points[$i * 2]." "; |
1826 | | | $pts .= $points[$i * 2+1]." "; |
1827 | | | } |
1828 | | | |
1829 | | | metadump("POLY $num_points ".$pts." $r $g $b $a"); |
1830 | | | |
1831 | | | return(imagepolygon($image, $points, $num_points, $color)); |
1832 | | | } |
1833 | | | |
1834 | | | function wimagefilledpolygon($image, $points, $num_points, $color) |
1835 | | | { |
1836 | | | if ($color===NULL) return; |
1837 | | | |
1838 | | | $col = imagecolorsforindex($image, $color); |
1839 | | | $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; |
1840 | | | $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; |
1841 | | | |
1842 | | | $pts = ""; |
1843 | | | for ($i=0; $i < $num_points; $i++) |
1844 | | | { |
1845 | | | $pts .= $points[$i * 2]." "; |
1846 | | | $pts .= $points[$i * 2+1]." "; |
1847 | | | } |
1848 | | | |
1849 | | | metadump("FPOLY $num_points ".$pts." $r $g $b $a"); |
1850 | | | |
1851 | | | return(imagefilledpolygon($image, $points, $num_points, $color)); |
1852 | | | } |
1853 | | | |
1854 | | | function wimagecreatetruecolor($width, $height) |
1855 | | | { |
1856 | | | |
1857 | | | |
1858 | | | metadump("BLANKIMAGE $width $height"); |
1859 | | | |
1860 | | | return imagecreatetruecolor($width,$height); |
1861 | | | |
1862 | | | } |
1863 | | | |
1864 | | | function wimagettftext($image, $size, $angle, $x, $y, $color, $file, $string) |
1865 | | | { |
1866 | | | if ($color===NULL) return; |
1867 | | | |
1868 | | | $col = imagecolorsforindex($image, $color); |
1869 | | | $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha']; |
1870 | | | $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127; |
1871 | | | |
1872 | | | metadump("TEXT $x $y $angle $size $file $r $g $b $a $string"); |
1873 | | | |
1874 | | | return(imagettftext($image, $size, $angle, $x, $y, $color, $file, $string)); |
1875 | | | } |
1876 | | | |
1877 | | | function wm_draw_marker_diamond($im, $col, $x, $y, $size=10) |
1878 | | | { |
1879 | | | $points = array(); |
1880 | | | |
1881 | | | $points []= $x-$size; |
1882 | | | $points []= $y; |
1883 | | | |
1884 | | | $points []= $x; |
1885 | | | $points []= $y-$size; |
1886 | | | |
1887 | | | $points []= $x+$size; |
1888 | | | $points []= $y; |
1889 | | | |
1890 | | | $points []= $x; |
1891 | | | $points []= $y+$size; |
1892 | | | |
1893 | | | $num_points = 4; |
1894 | | | |
1895 | | | imagepolygon($im, $points, $num_points, $col); |
1896 | | | } |
1897 | | | |
1898 | | | function wm_draw_marker_box($im, $col, $x, $y, $size=10) |
1899 | | | { |
1900 | | | $points = array(); |
1901 | | | |
1902 | | | $points []= $x-$size; |
1903 | | | $points []= $y-$size; |
1904 | | | |
1905 | | | $points []= $x+$size; |
1906 | | | $points []= $y-$size; |
1907 | | | |
1908 | | | $points []= $x+$size; |
1909 | | | $points []= $y+$size; |
1910 | | | |
1911 | | | $points []= $x-$size; |
1912 | | | $points []= $y+$size; |
1913 | | | |
1914 | | | $num_points = 4; |
1915 | | | |
1916 | | | imagepolygon($im, $points, $num_points, $col); |
1917 | | | } |
1918 | | | |
1919 | | | function wm_draw_marker_circle($im, $col, $x, $y, $size=10) |
1920 | | | { |
1921 | | | imagearc($im,$x, $y ,$size,$size,0,360,$col); |
1922 | | | } |
1923 | | | |
1924 | | | function draw_spine_chain($im,$spine,$col, $size=10) |
1925 | | | { |
1926 | | | $newn = count($spine); |
1927 | | | |
1928 | | | for ($i=0; $i < $newn; $i++) |
1929 | | | { |
1930 | | | imagearc($im,$spine[$i][X],$spine[$i][Y],$size,$size,0,360,$col); |
1931 | | | } |
1932 | | | } |
1933 | | | |
1934 | | | function dump_spine($spine) |
1935 | | | { |
1936 | | | print "===============\n"; |
1937 | | | for($i=0; $i<count($spine); $i++) |
1938 | | | { |
1939 | | | printf (" %3d: %d,%d (%d)\n", $i, $spine[$i][X], $spine[$i][Y], $spine[$i][DISTANCE] ); |
1940 | | | } |
1941 | | | print "===============\n"; |
1942 | | | } |
1943 | | | |
1944 | | | function draw_spine($im, $spine,$col) |
1945 | | | { |
1946 | | | $max_i = count($spine)-1; |
1947 | | | |
1948 | | | for ($i=0; $i <$max_i; $i++) |
1949 | | | { |
1950 | | | imageline($im, |
1951 | | | $spine[$i][X],$spine[$i][Y], |
1952 | | | $spine[$i+1][X],$spine[$i+1][Y], |
1953 | | | $col |
1954 | | | ); |
1955 | | | } |
1956 | | | } |
1957 | | | |
1958 | | | // vim:ts=4:sw=4: |
1959 | | | ?> |