Mercurial > octave-nkf
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") |