comparison src/graphics.cc @ 7827:3584f37eac69

better tick and limit handling, still missing logscale support * * * better adjustments of ticks for manual limits
author Shai Ayal <shaiay@sourceforge.net>
date Thu, 14 Feb 2008 06:33:29 +0200
parents adb520646d7e
children 4739b6a1925c
comparison
equal deleted inserted replaced
7826:68550ad9ee9c 7827:3584f37eac69
2239 val = data.min_pos (); 2239 val = data.min_pos ();
2240 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos) 2240 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
2241 min_pos = val; 2241 min_pos = val;
2242 } 2242 }
2243 2243
2244 // magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
2245 // integral.
2246
2247 static void magform (double x, double& a, int& b)
2248 {
2249 if (x == 0)
2250 {
2251 a = 0;
2252 b = 0;
2253 }
2254 else
2255 {
2256 double l = std::log10 (std::abs (x));
2257 double r = std::fmod (l, 1.);
2258 a = std::pow (10.0, r);
2259 b = static_cast<int> (l-r);
2260 if (a < 1)
2261 {
2262 a *= 10;
2263 b -= 1;
2264 }
2265
2266 if (x < 0)
2267 a = -a;
2268 }
2269 }
2270
2271 // A translation from Tom Holoryd's python code at
2272 // http://kurage.nimh.nih.gov/tomh/tics.py
2273 // FIXME -- add log ticks
2274
2275 double
2276 axes::properties::calc_tick_sep (double lo, double hi)
2277 {
2278 int ticint = 5;
2279
2280 // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
2281 // SCALE3 for Determination of Scales on Computer Generated
2282 // Plots", Communications of the ACM, 10 (1973), 639-640.
2283 // Also cited as ACM Algorithm 463.
2284
2285 double a;
2286 int b, x;
2287
2288 magform ((hi-lo)/ticint, a, b);
2289
2290 static const double sqrt_2 = sqrt (2.0);
2291 static const double sqrt_10 = sqrt (10.0);
2292 static const double sqrt_50 = sqrt (50.0);
2293
2294 if (a < sqrt_2)
2295 x = 1;
2296 else if (a < sqrt_10)
2297 x = 2;
2298 else if (a < sqrt_50)
2299 x = 5;
2300 else
2301 x = 10;
2302
2303 return x * std::pow (10., b);
2304
2305 }
2306
2244 // Attempt to make "nice" limits from the actual max and min of the 2307 // Attempt to make "nice" limits from the actual max and min of the
2245 // data. For log plots, we will also use the smallest strictly positive 2308 // data. For log plots, we will also use the smallest strictly positive
2246 // value. 2309 // value.
2247 2310
2248 static Matrix 2311 Matrix
2249 get_axis_limits (double xmin, double xmax, double min_pos, bool logscale) 2312 axes::properties::get_axis_limits (double xmin, double xmax, double min_pos, bool logscale)
2250 { 2313 {
2251 Matrix retval; 2314 Matrix retval;
2252 2315
2253 double min_val = xmin; 2316 double min_val = xmin;
2254 double max_val = xmax; 2317 double max_val = xmax;
2288 else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON)) 2351 else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
2289 { 2352 {
2290 min_val -= 0.1 * std::abs (min_val); 2353 min_val -= 0.1 * std::abs (min_val);
2291 max_val += 0.1 * std::abs (max_val); 2354 max_val += 0.1 * std::abs (max_val);
2292 } 2355 }
2293 // FIXME -- to do a better job, we should consider the tic spacing. 2356
2294 double scale = pow (10, floor (log10 (max_val - min_val) - 1)); 2357 double tick_sep = calc_tick_sep (min_val , max_val);
2295 min_val = scale * floor (min_val / scale); 2358 min_val = tick_sep * std::floor (min_val / tick_sep);
2296 max_val = scale * ceil (max_val / scale); 2359 max_val = tick_sep * ceil (max_val / tick_sep);
2297 } 2360 }
2298 } 2361 }
2299 2362
2300 retval.resize (1, 2); 2363 retval.resize (1, 2);
2301 2364
2303 retval(1) = max_val; 2366 retval(1) = max_val;
2304 2367
2305 return retval; 2368 return retval;
2306 } 2369 }
2307 2370
2308 // magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
2309 // integral.
2310
2311 void 2371 void
2312 axes::properties::magform (double x, double& a, int& b) 2372 axes::properties::calc_ticks_and_lims (array_property& lims, array_property& ticks, bool limmode_is_auto)
2313 { 2373 {
2314 if (x == 0) 2374
2315 { 2375 // FIXME -- add log ticks and lims
2316 a = 0;
2317 b = 0;
2318 }
2319 else
2320 {
2321 double l = std::log10 (std::abs (x));
2322 double r = std::fmod (l, 1.);
2323 a = std::pow (10.0, r);
2324 b = static_cast<int> (l-r);
2325 if (a < 1)
2326 {
2327 a *= 10;
2328 b -= 1;
2329 }
2330
2331 if (x < 0)
2332 a = -a;
2333 }
2334 }
2335
2336 // A translation from Tom Holoryd's python code at
2337 // http://kurage.nimh.nih.gov/tomh/tics.py
2338 // FIXME -- add log ticks
2339
2340 void
2341 axes::properties::calc_ticks (const array_property& lims, array_property& ticks)
2342 {
2343 int ticint = 5;
2344 2376
2345 if (lims.get ().is_empty ()) 2377 if (lims.get ().is_empty ())
2346 return; 2378 return;
2347 2379
2348 double lo = (lims.get ().matrix_value ()) (0); 2380 double lo = (lims.get ().matrix_value ()) (0);
2349 double hi = (lims.get ().matrix_value ()) (1); 2381 double hi = (lims.get ().matrix_value ()) (1);
2350 2382
2351 // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and 2383 double tick_sep = calc_tick_sep (lo , hi);
2352 // SCALE3 for Determination of Scales on Computer Generated 2384
2353 // Plots", Communications of the ACM, 10 (1973), 639-640. 2385 int i1 = static_cast<int> (std::floor (lo / tick_sep));
2354 // Also cited as ACM Algorithm 463. 2386 int i2 = static_cast<int> (std::ceil (hi / tick_sep));
2355 2387
2356 double a; 2388 if (limmode_is_auto)
2357 int b, x; 2389 {
2358 2390 // adjust limits to include min and max tics
2359 magform ((hi-lo)/ticint, a, b); 2391 Matrix tmp_lims (1,2);
2360 2392 tmp_lims(0) = tick_sep * i1;
2361 static const double sqrt_2 = sqrt (2.0); 2393 tmp_lims(1) = tick_sep * i2;
2362 static const double sqrt_10 = sqrt (10.0); 2394
2363 static const double sqrt_50 = sqrt (50.0); 2395 lims = tmp_lims;
2364 2396 }
2365 if (a < sqrt_2) 2397 else
2366 x = 1; 2398 {
2367 else if (a < sqrt_10) 2399 // adjust min and max tics if they are out of limits
2368 x = 2; 2400 i1 = static_cast<int> (std::ceil (lo / tick_sep));
2369 else if (a < sqrt_50) 2401 i2 = static_cast<int> (std::floor (hi / tick_sep));
2370 x = 5; 2402 }
2371 else 2403
2372 x = 10; 2404 Matrix tmp_ticks (1, i2-i1+1);
2373 2405 for (int i = 0; i <= i2-i1; i++)
2374 double sep = x * std::pow (10., b); 2406 tmp_ticks (i) = tick_sep * (i+i1);
2375 2407
2376 // FIXME x can now be used to set minor ticks 2408 ticks = tmp_ticks;
2377 if (x == 10)
2378 x = 1;
2379
2380 // The following guarantees that if zero is in the range, it will be
2381 // included as a tic.
2382
2383 int i1 = static_cast<int> (std::floor (lo / sep));
2384 int i2 = static_cast<int> (std::ceil (hi / sep));
2385
2386 Matrix limits (1, i2-i1+1);
2387 for (int i = 0; i < i2-i1+1; i++)
2388 limits (i) = sep*(i+i1);
2389
2390 ticks = limits;
2391 } 2409 }
2392 2410
2393 static bool updating_axis_limits = false; 2411 static bool updating_axis_limits = false;
2394 2412
2395 void 2413 void
2436 check_limit_vals (min_val, max_val, min_pos, xudata); 2454 check_limit_vals (min_val, max_val, min_pos, xudata);
2437 } 2455 }
2438 } 2456 }
2439 } 2457 }
2440 2458
2441 limits = get_axis_limits (min_val, max_val, min_pos, 2459 limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
2442 xproperties.xscale_is ("log")); 2460 xproperties.xscale_is ("log"));
2443 2461
2444 update_type = 'x'; 2462 update_type = 'x';
2445 } 2463 }
2446 } 2464 }
2447 else if (axis_type == "ydata" || axis_type == "yscale" 2465 else if (axis_type == "ydata" || axis_type == "yscale"
2470 check_limit_vals (min_val, max_val, min_pos, udata); 2488 check_limit_vals (min_val, max_val, min_pos, udata);
2471 } 2489 }
2472 } 2490 }
2473 } 2491 }
2474 2492
2475 limits = get_axis_limits (min_val, max_val, min_pos, 2493 limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
2476 xproperties.yscale_is ("log")); 2494 xproperties.yscale_is ("log"));
2477 2495
2478 update_type = 'y'; 2496 update_type = 'y';
2479 } 2497 }
2480 } 2498 }
2481 else if (axis_type == "zdata" || axis_type == "zscale" 2499 else if (axis_type == "zdata" || axis_type == "zscale"
2493 2511
2494 check_limit_vals (min_val, max_val, min_pos, zdata); 2512 check_limit_vals (min_val, max_val, min_pos, zdata);
2495 } 2513 }
2496 } 2514 }
2497 2515
2498 limits = get_axis_limits (min_val, max_val, min_pos, 2516 limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
2499 xproperties.zscale_is ("log")); 2517 xproperties.zscale_is ("log"));
2500 2518
2501 update_type = 'z'; 2519 update_type = 'z';
2502 } 2520 }
2503 } 2521 }
2504 else if (axis_type == "cdata" || axis_type == "climmode") 2522 else if (axis_type == "cdata" || axis_type == "climmode")