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
|