1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
|
include <gset.h>
include <mach.h>
include <math.h>
include <math/curfit.h>
include "wcslab.h"
include "wcs_desc.h"
# WL_SETUP -- Determine all the basic characteristics of the plot.
#
# Description
# Determine basic characteristics of the plot at hand. This involved
# "discovering" what part of the world system covers the screen, the
# orientation of the world to logical systems, what type of graph will
# be produced, etc. Many of the parameters determined here can be
# over-ridden by user-specified values.
procedure wl_setup (wd)
pointer wd # I: the WCSLAB descriptor
bool north
double array[N_EDGES,N_DIM], max_value[N_DIM], min_value[N_DIM]
double range[N_DIM], pole_position[N_DIM], view_edge[N_EDGES,N_DIM]
double wl_coord_rotation()
pointer mw_sctran()
string logtran "logical"
string wrldtran "world"
begin
# Calculate the transformations from the Logical (pixel space) system
# to the World (possibly anything) system and back.
WL_LWCT(wd) = mw_sctran (WL_MW(wd), logtran, wrldtran, AXIS)
WL_WLCT(wd) = mw_sctran (WL_MW(wd), wrldtran, logtran, AXIS)
# Indicate whether the center of the transformation is north.
if (WL_SYSTEM_TYPE(wd) == RA_DEC)
north = (WL_WORLD_CENTER(wd,LATITUDE) > 0.0D0)
# Determine the poles position.
if (WL_SYSTEM_TYPE(wd) == RA_DEC)
call wl_pole_position (WL_WLCT(wd), WL_AXIS_FLIP(wd),
WL_WORLD_CENTER(wd,LONGITUDE), north, WL_SYSTEM_TYPE(wd),
pole_position)
# Determine graph type based on the system type.
call wl_determine_graph_type (WL_SYSTEM_TYPE(wd), pole_position,
WL_SCREEN_BOUNDARY(wd,1), WL_GRAPH_TYPE(wd))
# Now find the extent of the WCS the window views, by constructing
# x,y vectors containing evenly spaced points around the edges of
# the viewing window.
call wl_construct_edge_vectors (WL_SCREEN_BOUNDARY(wd,1),
view_edge[1,X_DIM], view_edge[1,Y_DIM], N_EDGES)
# Find the range of the axes over the graphics viewport.
call wl_l2wd (WL_LWCT(wd), WL_AXIS_FLIP(wd), view_edge[1,X_DIM],
view_edge[1,Y_DIM], array[1,AXIS1], array[1,AXIS2], N_EDGES)
call alimd (array[1,AXIS1], N_EDGES, min_value[AXIS1], max_value[AXIS1])
call alimd (array[1,AXIS2], N_EDGES, min_value[AXIS2], max_value[AXIS2])
range[AXIS1] = abs (max_value[AXIS1] - min_value[AXIS1])
range[AXIS2] = abs (max_value[AXIS2] - min_value[AXIS2])
# The above isn't good enough for the sky projections. Deal with those.
if (WL_SYSTEM_TYPE(wd) == RA_DEC)
call wl_sky_extrema (wd, array[1,AXIS1], N_EDGES, pole_position,
north, min_value[AXIS1], max_value[AXIS1], range[AXIS1],
min_value[AXIS2], max_value[AXIS2], range[AXIS2])
# Determine the rotation between the systems.
WL_ROTA(wd) = wl_coord_rotation (WL_WLCT(wd), WL_AXIS_FLIP(wd),
WL_WORLD_CENTER(wd,AXIS1), max_value[AXIS2],
WL_WORLD_CENTER(wd,AXIS1), min_value[AXIS2])
# Round the intervals. This is done to make the labelling "nice" and
# to smooth edge effects.
if (IS_INDEFD (WL_MAJOR_INTERVAL(wd,AXIS1)) ||
IS_INDEFD (WL_BEGIN(wd,AXIS1)) || IS_INDEFD (WL_END(wd,AXIS1)))
call wl_round_axis (wd, AXIS1, min_value[AXIS1], max_value[AXIS1],
range[AXIS1])
if (IS_INDEFD (WL_MAJOR_INTERVAL(wd,AXIS2)) ||
IS_INDEFD (WL_BEGIN(wd,AXIS2)) || IS_INDEFD (WL_END(wd,AXIS2)))
call wl_round_axis (wd, AXIS2, min_value[AXIS2], max_value[AXIS2],
range[AXIS2])
end
# WL_POLE_POSITION -- Determine logical coordinates of a pole.
#
# Description
# Calculate the pole's position in the Logical system.
#
# Bugs
# Can only deal with Right Ascension/Declination.
procedure wl_pole_position (wlct, flip, longitude, north, system_type,
pole_position)
pointer wlct # I: the world-to-logical transformation
int flip # I: true if the axes are transposed
double longitude # I: the longitude to determine latitude
bool north # I: true if the pole is in the north
int system_type # I: type of system being examined
double pole_position[N_DIM] # O: the pole's logical coordinates
double sgn
begin
switch (system_type) {
# For Right Ascension/Declination, the pole is at any longitude but
# at only 90 degrees (north) or -90 degrees (south) latitude.
case RA_DEC:
if (north)
sgn = NORTH_POLE_LATITUDE
else
sgn = SOUTH_POLE_LATITUDE
call wl_w2ld (wlct, flip, longitude, sgn, pole_position[X_DIM],
pole_position[Y_DIM], 1)
}
# Sanity check on the pole position. It is very likely that there is
# no valid position in pixel space for the pole. This is checked for
# by looking for extremely large numbers.
if (abs (pole_position[X_DIM]) > abs (double (MAX_INT)))
pole_position[X_DIM] = real (MAX_INT)
if (abs (pole_position[Y_DIM]) > abs (double (MAX_INT)))
pole_position[Y_DIM] = real (MAX_INT)
end
# How close can the pole be to the center of the screen to be near-polar.
define HOW_CLOSE 3.
# WL_DETERMINE_GRAPH_TYPE -- Determine the actual graph type.
procedure wl_determine_graph_type (system_type, pole_position,
screen_boundary, graph_type)
int system_type # I: the type of WCS being dealt with
double pole_position[N_DIM] # I: the location of the pole
double screen_boundary[N_SIDES] # I: the edges of the display
int graph_type # O: the graph type
double max_dist, pole_dist, xcen, ycen
begin
# Determine graph type based on axis type.
switch (system_type) {
# If the pole is on the graph then force a graph_type of polar.
case RA_DEC:
xcen = (screen_boundary[LEFT] + screen_boundary[RIGHT]) / 2.
ycen = (screen_boundary[BOTTOM] + screen_boundary[TOP]) / 2.
max_dist = min ((screen_boundary[LEFT] - xcen) ** 2,
(screen_boundary[TOP] - ycen)**2)
pole_dist = (pole_position[X_DIM] - xcen) ** 2 +
(pole_position[Y_DIM] - ycen) ** 2
# Check to see whether the graph is "polar", "near_polar"
# or "normal". If the pole lies within middle part of the
# viewport, then the graph is "polar". If the pole is within
# a certain maximum distance then it is "near_polar".
# Otherwise it is normal.
switch (graph_type) {
case NORMAL:
# do nothing
case POLAR:
# do nothing
case NEAR_POLAR:
# do nothing
default:
if (pole_dist < max_dist)
graph_type = POLAR
else if (pole_dist < HOW_CLOSE * max_dist)
graph_type = NEAR_POLAR
else
graph_type = NORMAL
}
# For all other cases, explicitely set this to normal.
default:
graph_type = NORMAL
}
end
# WL_CONSTRUCT_EDGE_VECTORS -- Construct vectors of values along window's edge.
#
# Description
# This routines filles two arrays, with the x-values and y-values of
# evenly spaced points along the edges of the screen. This is used to
# make transformation of the logical edges into the world system
# more convenient.
procedure wl_construct_edge_vectors (screen_boundary, x, y, vector_size)
double screen_boundary[N_SIDES] # I: the side values
double x[vector_size], y[vector_size] # O: the edge vector points
int vector_size # I: the number of edge vector points
double current, interval
int i, left_over, offset1, offset2, side_length
begin
# Divide the vectors into equal amounts for each side.
side_length = vector_size / N_SIDES
left_over = mod (vector_size, N_SIDES)
# Calculate the horizontal components.
interval = (screen_boundary[RIGHT] - screen_boundary[LEFT]) /
side_length
current = screen_boundary[LEFT]
offset1 = side_length
for (i = 1; i <= side_length; i = i + 1) {
x[i] = current + interval
y[i] = screen_boundary[BOTTOM]
x[i+offset1] = current
y[i+offset1] = screen_boundary[TOP]
current = current + interval
}
# Calculate the verticle components.
interval = (screen_boundary[TOP] - screen_boundary[BOTTOM]) /
side_length
current = screen_boundary[BOTTOM]
offset1 = 2 * side_length
offset2 = 3 * side_length
for (i = 1; i <= side_length; i = i + 1) {
x[i+offset1] = screen_boundary[LEFT]
y[i+offset1] = current
x[i+offset2] = screen_boundary[RIGHT]
y[i+offset2] = current + interval
current = current + interval
}
# Fill in the left over with a single point.
offset1 = 4 * side_length
for (i = 1; i <= left_over; i = i + 1) {
x[i+offset1] = screen_boundary[LEFT]
y[i+offset1] = screen_boundary[BOTTOM]
}
end
# WL_SKY_EXTREMA -- Determine what range the view window covers in the sky.
# This routine is only called if the WCS RA,DEC.
#
# Description
# Because of the different graph types and the fact that axis 1 usually
# wraps, more work needs to be done to determine what part of the sky
# is covered by the viewing window.
procedure wl_sky_extrema (wd, ax1_array, n_points, pole_position, north,
ax1min, ax1max, ax1ran, ax2min, ax2max, ax2ran)
pointer wd # I: the WCSLAB descriptor
double ax1_array[n_points] # I: the axis 1 edge vector
int n_points # I: the length of the edge vector
double pole_position[N_DIM] # I: the pole position
bool north # I: is the pole in the north ?
double ax1min, ax1max, ax1ran # I/O: the minimum, maximum, range in axis 1
double ax2min, ax2max, ax2ran # I/O: the minimum, maximum, range in axis 2
bool is_pole
double nx, ny, xcen, ycen
int wl_direction_from_axis1(), wl_find_side(), wl_opposite_side()
begin
# Is the pole on the graph ?
if ((pole_position[X_DIM] < WL_SCREEN_BOUNDARY(wd,LEFT)) ||
(pole_position[X_DIM] > WL_SCREEN_BOUNDARY(wd,RIGHT)) ||
(pole_position[Y_DIM] < WL_SCREEN_BOUNDARY(wd,BOTTOM)) ||
(pole_position[Y_DIM] > WL_SCREEN_BOUNDARY(wd,TOP)))
is_pole = false
else
is_pole = true
# If so adjust the RA and DEC ranges appropriately.
if (is_pole) {
# Set the RA range.
ax1min = 0.0D0
ax1max = 359.9D0
ax1ran = 360.0D0
# Set the dec range.
if (north)
ax2max = NORTH_POLE_LATITUDE - ((NORTH_POLE_LATITUDE -
ax2min) * DISTANCE_TO_POLE )
else
ax2min = SOUTH_POLE_LATITUDE + ((NORTH_POLE_LATITUDE +
ax2max) * DISTANCE_TO_POLE)
ax2ran = abs (ax2max - ax2min)
# Mark the pole.
call gmark (WL_GP(wd), real (pole_position[X_DIM]),
real (pole_position[Y_DIM]), POLE_MARK_SHAPE, POLE_MARK_SIZE,
POLE_MARK_SIZE)
} else {
# Only the RA range needs adjusting.
call wl_ra_range (ax1_array, n_points, ax1min, ax1max, ax1ran)
}
# Adjust the labelling characteristics appropritatley for various
# types of graphs.
if (WL_GRAPH_TYPE(wd) == POLAR) {
# Determine which direction the axis 2's will be labeled on polar
# graphs.
if (IS_INDEFD (WL_POLAR_LABEL_POSITION(wd))) {
call wl_get_axis2_label_direction (WL_LWCT(wd),
WL_AXIS_FLIP(wd), pole_position, WL_SCREEN_BOUNDARY(wd,1),
WL_POLAR_LABEL_POSITION(wd), WL_BAD_LABEL_SIDE(wd))
} else {
WL_BAD_LABEL_SIDE(wd) = wl_direction_from_axis1 (WL_WLCT(wd),
WL_AXIS_FLIP(wd), pole_position, north,
WL_POLAR_LABEL_POSITION(wd), WL_BEGIN(wd,AXIS2),
WL_END(wd,AXIS2), WL_SCREEN_BOUNDARY(wd,1))
if (IS_INDEFI (WL_BAD_LABEL_SIDE(wd)))
WL_BAD_LABEL_SIDE(wd) = BOTTOM
}
# If the graph type is polar, then determine how to justify
# the labels.
if (IS_INDEFI (WL_POLAR_LABEL_DIRECTION(wd)))
WL_POLAR_LABEL_DIRECTION(wd) =
wl_opposite_side (WL_BAD_LABEL_SIDE(wd))
# If the graph_type is near-polar, then handle the directions a bit
# differently.
} else if (WL_GRAPH_TYPE(wd) == NEAR_POLAR) {
# Find the side that the pole is on.
xcen = (WL_SCREEN_BOUNDARY(wd,LEFT) +
WL_SCREEN_BOUNDARY(wd,RIGHT)) / 2.
ycen = (WL_SCREEN_BOUNDARY(wd,BOTTOM) +
WL_SCREEN_BOUNDARY(wd,TOP)) / 2.
call wl_axis_on_line (xcen, ycen, pole_position[X_DIM],
pole_position[Y_DIM], WL_SCREEN_BOUNDARY(wd,1), nx, ny)
if (IS_INDEFD(nx) || IS_INDEFD(ny)) {
WL_BAD_LABEL_SIDE(wd) = BOTTOM
WL_POLAR_LABEL_DIRECTION(wd) = LEFT
} else {
WL_BAD_LABEL_SIDE(wd) = wl_find_side (nx, ny,
WL_SCREEN_BOUNDARY(wd,1))
if (WL_BAD_LABEL_SIDE(wd) == LEFT || WL_BAD_LABEL_SIDE(wd) ==
RIGHT)
if (abs (ny - WL_SCREEN_BOUNDARY(wd,BOTTOM)) <
abs (ny - WL_SCREEN_BOUNDARY(wd,TOP)))
WL_POLAR_LABEL_DIRECTION(wd) = BOTTOM
else
WL_POLAR_LABEL_DIRECTION(wd) = TOP
else
if (abs (nx - WL_SCREEN_BOUNDARY(wd,LEFT)) <
abs (nx - WL_SCREEN_BOUNDARY(wd,RIGHT)))
WL_POLAR_LABEL_DIRECTION(wd) = LEFT
else
WL_POLAR_LABEL_DIRECTION(wd) = RIGHT
}
}
end
# WL_COORD_ROTATION -- Determine "rotation" between the coordinate systems.
#
# Description
# This routine takes the world-to-logical coordinate transformation and
# two points in the world system which should define the positive verticle
# axis in the world system. These points are translated into the logical
# system and the angle between the logical vector and its positive verticle
# vector is calculated and returned. The rotation angle is returned
# in degrees and is always positive.
double procedure wl_coord_rotation (wlct, flip, wx1, wy1, wx2, wy2)
pointer wlct # I: the world-to-logical transformation
int flip # I: true if the coordinates are transposed
double wx1, wy1, wx2, wy2 # I: points in world space to figure rotation from
double delx, dely, rota, x1, y1, x2, y2
bool fp_equald()
begin
# Transform the points to the logical system.
call wl_w2ld (wlct, flip, wx1, wy1, x1, y1, 1)
call wl_w2ld (wlct, flip, wx2, wy2, x2, y2, 1)
# Determine the rotation.
delx = x2 - x1
dely = y2 - y1
if (fp_equald (delx, 0.0D0) && fp_equald (dely, 0.0D0))
rota = 0.
else
rota = RADTODEG (atan2 (dely, delx))
if (rota < 0.0D0)
rota = rota + FULL_CIRCLE
return (rota)
end
# Define how many axis one should go for.
define RA_NUM_TRY 6
define DEC_NUM_TRY 6
define DEC_POLAR_NUM_TRY 4
# WL_ROUND_AXIS - Round values for the axis.
procedure wl_round_axis (wd, axis, minimum, maximum, range)
pointer wd # I: the WCSLAB descriptor
int axis # I: the axis being worked on
double minimum, maximum, range # I: raw values to be rounded
int num_try
begin
# Depending on axis type, round the values.
switch (WL_SYSTEM_TYPE(wd)) {
case RA_DEC:
if (axis == LONGITUDE)
call wl_round_ra (minimum, maximum, range, RA_NUM_TRY,
WL_BEGIN(wd,LONGITUDE), WL_END(wd,LONGITUDE),
WL_MAJOR_INTERVAL(wd,LONGITUDE))
else {
if (WL_GRAPH_TYPE(wd) == POLAR)
num_try = DEC_POLAR_NUM_TRY
else
num_try = DEC_NUM_TRY
call wl_round_dec (minimum, maximum, range, num_try,
WL_BEGIN(wd,LATITUDE), WL_END(wd,LATITUDE),
WL_MAJOR_INTERVAL(wd,LATITUDE))
}
default:
call wl_generic_round (minimum, maximum, range, WL_BEGIN(wd,axis),
WL_END(wd,axis), WL_MAJOR_INTERVAL(wd,axis))
}
end
# WL_GET_AXIS2_LABEL_DIRECTION -- Dertermine label direction for latitides.
#
# Description
# Determine from which edge of the graph the axis 2 labels are to
# appear. This (in general) is the opposite edge from which the pole
# is nearest to. Move the pole to the closest edges, determine which
# side it is, then chose the direction as the opposite. Also determines
# the Axis 1 at which the Axis 2 labels will appear.
procedure wl_get_axis2_label_direction (lwct, flip, pole_position,
screen_boundary, pole_label_position, bad_label_side)
pointer lwct # I: logical-to-world transformation
int flip # I: true if the axis are transposed
double pole_position[N_DIM] # I: the position of the pole
double screen_boundary[N_SIDES] # I: the edges of the screen
double pole_label_position # O: the axis 1 that axis 2 labels should
# appear for polar|near-polar graphs
int bad_label_side # O: side not to place axis 1 labels
double dif, tdif, dummy
begin
# Determine which direction, up or down, the axis 2's will be labelled.
dif = abs (screen_boundary[TOP] - pole_position[AXIS2])
bad_label_side= TOP
tdif = abs (screen_boundary[BOTTOM] - pole_position[AXIS2])
if (tdif < dif) {
dif = tdif
bad_label_side = BOTTOM
}
# Determine at what value of Axis 1 the Axis 2 labels should appear.
switch (bad_label_side) {
case TOP:
call wl_l2wd (lwct, flip, pole_position[AXIS1],
screen_boundary[BOTTOM], pole_label_position, dummy, 1)
case BOTTOM:
call wl_l2wd (lwct, flip, pole_position[AXIS1],
screen_boundary[TOP], pole_label_position, dummy, 1)
case LEFT:
call wl_l2wd (lwct, flip, screen_boundary[RIGHT],
pole_position[AXIS2], pole_label_position, dummy, 1)
case RIGHT:
call wl_l2wd (lwct, flip, screen_boundary[LEFT],
pole_position[AXIS2], pole_label_position, dummy, 1)
}
end
# WL_DIRECTION_FROM_AXIS1 -- Determine axis 2 label direction from axis 1.
#
# Function Returns
# This returns the side where Axis 1 should not be labelled.
int procedure wl_direction_from_axis1 (wlct, flip, pole_position, north,
polar_label_position, lbegin, lend, screen_boundary)
pointer wlct # I: world-to-logical transformation
int flip # I: true if the axes are transposed
double pole_position[N_DIM] # I: the pole position
bool north # I: true if the pole is the north pole
double polar_label_position # I: the axis 1 where axis 2 will be
# marked
double lbegin # I: low end of axis 2
double lend # I: high end of axis 2
double screen_boundary[N_SIDES] # I: the window boundary
double nx, ny, cx, cy
int wl_find_side()
begin
# Determine the point in logical space where the axis 1 and the
# minimum axis 2 meet.
if (north)
call wl_w2ld (wlct, flip, polar_label_position, lbegin, nx, ny, 1)
else
call wl_w2ld (wlct, flip, polar_label_position, lend, nx, ny, 1)
# This line should cross a window boundary. Find that point.
call wl_axis_on_line (pole_position[X_DIM], pole_position[Y_DIM],
screen_boundary, nx, ny, cx, cy)
# Get the side that the crossing point is. This is the axis 2 labelling
# direction.
if (IS_INDEFD(cx) || IS_INDEFD(cy))
return (INDEFI)
else
return (wl_find_side (cx, cy, screen_boundary))
end
# WL_OPPOSITE_SIDE - Return the opposite of the given side.
#
# Returns
# The opposite side of the specified side as follows:
# RIGHT -> LEFT
# LEFT -> RIGHT
# TOP -> BOTTOM
# BOTTOM -> TOP
int procedure wl_opposite_side (side)
int side # I: the side to find the opposite of
int new_side
begin
switch (side) {
case LEFT:
new_side = RIGHT
case RIGHT:
new_side = LEFT
case TOP:
new_side = BOTTOM
case BOTTOM:
new_side = TOP
}
return (new_side)
end
# Define whether things are on the screen boundary or on them.
define IN (($1>=screen_boundary[LEFT])&&($1<=screen_boundary[RIGHT])&&($2>=screen_boundary[BOTTOM])&&($2<=screen_boundary[TOP]))
# WL_AXIS_ON_LINE - Determine intersection of line and a screen boundary.
#
# Description
# Return the point where the line defined by the two input points
# crosses a screen boundary. The boundary is choosen by determining
# which one is between the two points.
procedure wl_axis_on_line (x0, y0, x1, y1, screen_boundary, nx, ny)
double x0, y0, x1, y1 # I: random points in space
double screen_boundary[N_SIDES] # I: sides of the window
double nx, ny # O: the closest point on a window boundary
double x_val[N_SIDES], y_val[N_SIDES], tx0, ty0, tx1, ty1, w[2]
int i
pointer cvx, cvy
double dcveval()
begin
# Get the line parameters.
x_val[1] = x0
x_val[2] = x1
y_val[1] = y0
y_val[2] = y1
iferr (call dcvinit (cvx, CHEBYSHEV, 2, min (x0, x1), max (x0, x1)))
cvx = NULL
else {
call dcvfit (cvx, x_val, y_val, w, 2, WTS_UNIFORM, i)
if (i != OK)
call error (i, "wlaxie: Error solving on X")
}
iferr (call dcvinit (cvy, CHEBYSHEV, 2, min (y0, y1), max (y0, y1)))
cvy = NULL
else {
call dcvfit (cvy, y_val, x_val, w, 2, WTS_UNIFORM, i)
if (i != OK)
call error (i, "wlaxie: Error solving on Y")
}
# Solve for each side.
x_val[LEFT] = screen_boundary[LEFT]
if (cvx == NULL)
y_val[LEFT] = screen_boundary[LEFT]
else
y_val[LEFT] = dcveval (cvx, x_val[LEFT])
x_val[RIGHT] = screen_boundary[RIGHT]
if (cvx == NULL )
y_val[RIGHT] = screen_boundary[RIGHT]
else
y_val[RIGHT] = dcveval (cvx, x_val[RIGHT])
y_val[TOP] = screen_boundary[TOP]
if (cvy == NULL)
x_val[TOP] = screen_boundary[TOP]
else
x_val[TOP] = dcveval (cvy, y_val[TOP])
y_val[BOTTOM] = screen_boundary[BOTTOM]
if (cvy == NULL)
x_val[BOTTOM] = screen_boundary[BOTTOM]
else
x_val[BOTTOM] = dcveval (cvy, y_val[BOTTOM])
# Rearrange the input points to be in ascending order.
if (x0 < x1) {
tx0 = x0
tx1 = x1
} else {
tx0 = x1
tx1 = x0
}
if (y0 < y1) {
ty0 = y0
ty1 = y1
} else {
ty0 = y1
ty1 = y0
}
# Now find which point is between the two given points and is within
# the viewing area.
# NOTE: Conversion to real for the check- if two points are so close
# for double, any of them would serve as the correct answer.
nx = INDEFD
ny = INDEFD
for (i = 1; i <= N_SIDES; i = i + 1)
if (real (tx0) <= real (x_val[i]) &&
real (x_val[i]) <= real (tx1) &&
real (ty0) <= real (y_val[i]) &&
real (y_val[i]) <= real (ty1) &&
IN (x_val[i], y_val[i]) ) {
nx = x_val[i]
ny = y_val[i]
}
# Release the curve fit descriptors.
if (cvx != NULL)
call dcvfree (cvx)
if (cvy != NULL)
call dcvfree (cvy)
end
# WL_FIND_SIDE -- Return the side that the given point is lying on.
#
# Function Returns
# Return the side, TOP, BOTTOM, LEFT, or RIGHT, that the specified
# point is lying on. One of the coordinates must be VERY CLOSE to one of
# the sides or INDEFI will be returned.
int procedure wl_find_side (x, y, screen_boundary)
double x, y # I: the point to inquire about
double screen_boundary[N_SIDES] # I: the edges of the screen
double dif, ndif
int side
begin
dif = abs (x - screen_boundary[LEFT])
side = LEFT
ndif = abs (x - screen_boundary[RIGHT])
if (ndif < dif) {
side = RIGHT
dif = ndif
}
ndif = abs (y - screen_boundary[BOTTOM])
if (ndif < dif) {
side = BOTTOM
dif = ndif
}
ndif = abs (y - screen_boundary[TOP])
if (ndif < dif)
side = TOP
return (side)
end
# WL_RA_RANGE -- Determine the range in RA given a list of possible values.
#
# Description
# Determine the largest range in RA from the provided list of values.
# The problem here is that it is unknown which way the graph is oriented.
# To simplify the problem, it is assume that the graph range does not extend
# beyond a hemisphere and that all distances in RA is less than a hemisphere.
# This assumption is needed to decide when the 0 hour is on the graph.
procedure wl_ra_range (ra, n_values, min, max, diff)
double ra[ARB] # I: the possible RA values
int n_values # I: the number of possible RA values
double min # I/O: the minimum RA
double max # I/O: the maximum RA
double diff # I/O: the difference between minimum and maximum
bool wrap
int i, j, n_diffs
pointer sp, max_array, min_array, ran_array
int wl_max_element_array()
begin
call smark (sp)
call salloc (max_array, n_values * n_values, TY_DOUBLE)
call salloc (min_array, n_values * n_values, TY_DOUBLE)
call salloc (ran_array, n_values * n_values, TY_DOUBLE)
# Check whether the RA is wrapped or not.
n_diffs = 0
do i = 1, n_values {
if (ra[i] >= min && ra[i] <= max)
next
n_diffs = n_diffs + 1
}
if (n_diffs > 0)
wrap = true
else
wrap = false
n_diffs = 0
for (i = 1; i <= n_values; i = i + 1) {
for (j = i + 1; j <= n_values; j = j + 1) {
n_diffs = n_diffs + 1
call wl_getradif (ra[i], ra[j], Memd[min_array+n_diffs-1],
Memd[max_array+n_diffs-1], Memd[ran_array+n_diffs-1],
wrap)
}
}
i = wl_max_element_array (Memd[ran_array], n_diffs)
min = Memd[min_array+i-1]
max = Memd[max_array+i-1]
diff = Memd[ran_array+i-1]
call sfree (sp)
end
# WL_GETRADIFF -- Get differences in RA based on degrees.
#
# Description
# This procedure determines, given two values in degrees, the minimum,
# maximum, and difference of those values. The assumption is that no
# difference should be greater than half a circle. Based on this assumption,
# a difference is found and the minimum and maximum are determined. The
# maximum can be greater than 360 degrees.
procedure wl_getradif (val1, val2, min, max, diff, wrap)
double val1, val2 # I: the RA values
double min, max # O: the min RA and max RA (possibly > 360.0)
double diff # O: the min, max difference
bool wrap # I: is the ra wrapped ?
begin
if (! wrap && (abs (val1 - val2) > HALF_CIRCLE))
if (val1 < val2) {
min = val2
max = val1 + FULL_CIRCLE
} else {
min = val1
max = val2 + FULL_CIRCLE
}
else
if (val1 < val2) {
min = val1
max = val2
} else {
min = val2
max = val1
}
diff = max - min
end
define NRAGAP 26
# WL_ROUND_RA -- Modify the RA limits and calculate an interval to label.
#
# Description
# The RA limits determine by just the extremes of the window ususally do
# not fall on "reasonable" boundaries; i.e. essentially they are random
# numbers. However, for labelling purposes, it is nice to have grids and
# tick marks for "rounded" numbers- For RA, this means values close to
# whole hours, minutes, or seconds. For example, if the span across the
# plot is a few hours, the marks and labels should represent simply whole
# hours. This routine determines new RA limits based on this and some
# interval to produce marks between the newly revised limits.
procedure wl_round_ra (longmin, longmax, longran, num_try, minimum, maximum,
major_interval)
double longmin # I: longitude minimum
double longmax # I: longitude maximum
double longran # I: longitude range
int num_try # I: the number of intervals to try for
double minimum # O: the minimum RA value (in degrees)
double maximum # O: the maximum RA value (in degrees)
double major_interval # O: the appropriate interval (in degrees) for the
# major line marks.
double ragap[NRAGAP]
double wl_check_arrayd(), wl_round_upd()
data ragap / 1.0D-4, 2.0D-4, 5.0D-4, 1.0D-3, 2.0D-3, 5.0D-3,
0.01D0, 0.02D0, 0.05D0, 0.1D0, 0.2D0, 0.5D0, 1.0D0,
2.0D0, 5.0D0, 10.0D0, 20.0D0, 30.0D0, 60.0D0, 120.0D0,
300.0D0, 600.0D0, 1.2D3, 1.8D3, 3.6D3, 7.2D3 /
begin
major_interval = wl_check_arrayd (DEGTOST (longran) / num_try,
ragap, NRAGAP)
minimum = STTODEG (wl_round_upd (DEGTOST (longmin), major_interval) -
major_interval)
maximum = STTODEG (wl_round_upd (DEGTOST (longmax), major_interval))
major_interval = STTODEG (major_interval)
end
define NDECGAP 28
# WL_ROUND_DEC -- Modify the DEC limits and calculate an interval to label.
#
# Description
# The DEC limits determine by just the extremes of the window ususally do
# not fall on "reasonable" boundaries; i.e. essentially they are random
# numbers. However, for labelling purposes, it is nice to have grids and
# tick marks for "rounded" numbers- For DEC, this means values close to
# whole degrees, minutes, or seconds. For example, if the span across the
# plot is a few degrees, the marks and labels should represent simply whole
# degrees. This routine determines new DEC limits based on this and some
# interval to produce marks between the newly revised limits.
procedure wl_round_dec (latmin, latmax, latran, num_try, minimum, maximum,
major_interval)
double latmin # I: the latitude minimum
double latmax # I: the latitude maximum
double latran # I: the latitude range
int num_try # I: number of intervals to try for
double minimum # O: the DEC minimum
double maximum # O: the DEC maximum
double major_interval # O: the labelling interval to use for major lines
double decgap[NDECGAP]
double wl_check_arrayd(), wl_round_upd()
data decgap / 1.0D-4, 2.0D-4, 5.0D-4, 1.0D-3, 2.0D-3, 5.0D-3,
0.01D0, 0.02D0, 0.05D0, 0.1D0, 0.2D0, 0.5D0, 1.0D0,
2.0D0, 5.0D0, 10.0D0,20.0D0, 30.0D0, 60.0D0, 120.0d0,
300.0D0, 600.0D0, 1.2D3, 1.8D3, 3.6D3, 7.2D3, 1.8D4, 3.6D4 /
begin
major_interval = wl_check_arrayd (DEGTOSA (latran) / num_try,
decgap, NDECGAP)
minimum = SATODEG (wl_round_upd (DEGTOSA (latmin), major_interval) -
major_interval)
maximum = SATODEG (wl_round_upd (DEGTOSA (latmax), major_interval))
major_interval = SATODEG (major_interval)
# Make sure that the grid marking does not include the pole.
maximum = min (maximum, NORTH_POLE_LATITUDE - major_interval)
minimum = max (minimum, SOUTH_POLE_LATITUDE + major_interval)
end
# WL_GENERIC_ROUND -- Round the values (if possible).
#
# History
# 7Feb91 - Created by Jonathan D. Eisenhamer, STScI.
procedure wl_generic_round (minimum, maximum, range, lbegin, lend, interval)
double minimum, maximum, range # I: the raw input values
double lbegin, lend # O: the begin and end label points
double interval # O: the major label interval
double amant, diff
int iexp, num
double wl_round_upd()
begin
diff = log10 (abs (range) / 4.D0)
iexp = int (diff)
if (diff < 0)
iexp = iexp - 1
amant = diff - double (iexp)
if (amant < 0.15D0)
num = 1
else if (amant < 0.50D0)
num = 2
else if (amant < 0.85D0)
num = 5
else
num = 10
interval = double (num) * 10.0D0 ** iexp
lbegin = wl_round_upd (minimum, interval) - interval
lend = wl_round_upd (maximum, interval)
end
# WL_ROUND_UPD -- Round X up to nearest whole multiple of Y.
double procedure wl_round_upd (x, y)
double x # I: value to be rounded
double y # I: multiple of X is to be rounded up in
double z, r
begin
if (x < 0.0D0)
z = 0.0D0
else
z = y
r = y * double (int ((x + z) / y))
return (r)
end
# WL_CHECK_ARRAYD -- Check proximity of array elements to each other.
#
# Description
# Returns the element of the array arr(n) which is closest to an exact
# value EX.
double procedure wl_check_arrayd (ex, arr, n)
double ex # I: the exact value
double arr[ARB] # I: the array of rounded values
int n # I: dimension of array of rounded values
int j
begin
for (j = 1; j < n && (ex - arr[j]) > 0.0D0; j = j + 1)
;
if (j > 1 && j < n)
if (abs (ex - arr[j-1]) < abs (ex - arr[j]))
j = j - 1
return (arr[j])
end
|