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
|
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
include <mach.h>
include <gio.h>
include "glabax.h"
# GLB_FIND_TICKS -- Find the optimal positions for the tick marks on an axis.
# If rounding is enabled, extend the WCS out to the next tick outside the
# boundary on either end of the axis. Since this routine may modify the WCS
# it must be called before any other routines (e.g., glb_setaxes). Our task
# is to position the major ticks in world coordinates at round numbers, e.g.,
# 10, 20, 30 for a linear scale or 10, 100, 1000 for a log scale. The minor
# ticks are evenly distributed over the range between the major ticks. If
# log scaling is in use the step size between ticks will change by an order
# of magnitude (by the factor KSTEP) in each decade of the log scale, i.e.,
# at each major tick. All tick positions and offsets are output in world
# coordinates.
procedure glb_find_ticks (gp, ap, ax1, ax2, angle)
pointer gp # graphics descriptor
pointer ap # axis parameters (from graphics descriptor)
pointer ax1, ax2 # axis descriptors (output)
int angle # axis orientation, 0 or 90 degrees
pointer w
int logflag, nminor, scaling, t1, t2
real char_height, char_width, wctick, tval
real p1, p2, tp1, tp2, wcp1, tick1, step, minor_step, length
bool fp_equalr()
int gt_ndigits()
real ggetr(), elogr(), aelogr(), glb_minorstep()
begin
w = GP_WCSPTR (gp, GP_WCS(gp))
# Start by zeroing the AX structures so that we do not have to zero
# fields explicitly. This is a bit tricky because we are implicitly
# setting fields that are not named, but it saves time and space.
call aclri (Memi[ax1], LEN_AX)
call aclri (Memi[ax2], LEN_AX)
# If ticks are not to be drawn or if there are fewer than 2 ticks then
# we are done.
if (GL_NMAJOR(ap) <= 2)
return
# Call the tick placement algorithm to determine where to put the major
# tick marks. The output of this block are the variables P1 and P1,
# the world coords of the ends of the axis, and TICK1 and STEP, the
# world coords of the first tick and separation in world coords between
# major ticks.
if (angle == 0) {
p1 = WCS_WX1(w)
p2 = WCS_WX2(w)
scaling = WCS_XTRAN(w)
} else {
p1 = WCS_WY1(w)
p2 = WCS_WY2(w)
scaling = WCS_YTRAN(w)
}
AX_SCALING(ax1) = scaling
AX_SCALING(ax2) = scaling
if (scaling == LOG) {
p1 = log10 (p1)
p2 = log10 (p2)
logflag = YES
} else if (scaling == ELOG) {
p1 = elogr (p1)
p2 = elogr (p2)
logflag = YES
} else
logflag = NO
# Call the tick placement algorithm.
call gtickr (p1, p2, GL_NMAJOR(ap), logflag, tick1, step)
# If rounding is enabled, extend the WCS out to the next major tick
# position outward on either end. Always round if log scaling.
if (GL_ROUND(ap) == YES || scaling != LINEAR) {
if (!fp_equalr (p1, tick1)) {
tick1 = tick1 - step
p1 = tick1
}
length = (p2 - p1) / step
if (!fp_equalr (p1 + int(length) * step, p2))
p2 = p1 + (int(length) + 1) * step
if (scaling == ELOG) {
tp1 = aelogr (p1)
tp2 = aelogr (p2)
} else if (scaling == LOG) {
tp1 = 10.0 ** p1
tp2 = 10.0 ** p2
} else {
tp1 = p1
tp2 = p2
}
if (angle == 0) {
WCS_WX1(w) = tp1
WCS_WX2(w) = tp2
} else {
WCS_WY1(w) = tp1
WCS_WY2(w) = tp2
}
GP_WCSSTATE(gp) = MODIFIED
}
# Compute the coords of the axis endpoint and of the first tick in world
# coords.
if (scaling == LINEAR) {
wctick = tick1
wcp1 = p1
} else if (scaling == LOG) {
wctick = 10.0 ** tick1
wcp1 = 10.0 ** p1
} else {
wctick = aelogr (tick1)
wcp1 = aelogr (p1)
}
# Compute the number of minor ticks. If we are log scaling there
# are either no minor ticks or 8 minor ticks. If the scaling is
# linear the tick placement algorithm is used to compute the best
# number of minor ticks, using GL_NMINOR as a close estimate. If
# NMINOR is negative automatic tick selection is disabled and exactly
# abs(NMINOR) ticks will be drawn. If NMINOR is zero no minor ticks
# are drawn.
if (GL_NMINOR(ap) == 0) # no minor ticks
nminor = 0
else if (logflag == YES) # log scaling
nminor = 8
else {
minor_step = glb_minorstep (tick1, tick1+step, GL_NMINOR(ap))
nminor = nint (abs (step / minor_step)) - 1
}
AX_NMINOR(ax1) = nminor
AX_NMINOR(ax2) = nminor
# Compute the step size in world coords between minor ticks and the
# number of minor ticks to be drawn initially until the first major
# tick (tick1) is reached. Note that for ELOG scaling the minor
# step size and number of minor ticks are different in the range
# +-10 (which is linear) than elsewhere, but we ignore that here.
if (scaling == LINEAR) {
minor_step = step / (nminor + 1)
AX_INLEFT(ax1) = abs (int ((wctick - wcp1) / minor_step))
} else {
t1 = nint (tick1)
t2 = nint (tick1 + step)
if (scaling == LOG)
minor_step = (10.0 ** t2 - 10.0 ** t1) / 9.
else
minor_step = (aelogr(real(t2)) - aelogr(real(t1))) / 9.
if (nminor == 0)
minor_step = minor_step * 9.
AX_INLEFT(ax1) = 0
}
AX_INLEFT(ax2) = AX_INLEFT(ax1)
# Set KSTEP, the adjustment to the step size at each major tick. This
# is always 1.0 if the scale is linear. Set KSTEP to negative if ELOG
# scaling, to tell the drawing code to invert kstep (.1->10 or 10->.1)
# when passing through the origin (necessary for ELOG scaling). The
# sign is not otherwise significant. If heading toward the origin
# initially then KSTEP is inverted for ELOG scaling vs LOG scaling.
if (scaling == LINEAR) {
AX_IKSTEP(ax1) = 1.0
} else if (scaling == ELOG) {
tval = p1
if (abs (tval + step) > abs(t1))
AX_IKSTEP(ax1) = -10.0
else
AX_IKSTEP(ax1) = -0.1
} else
AX_IKSTEP(ax1) = 10.0 ** step
AX_IKSTEP(ax2) = AX_IKSTEP(ax1)
# Set those parameters which differ depending on whether the axis is
# horizontal or vertical.
if (angle == 0) {
AX_TICK1(ax1,1) = wctick - (AX_INLEFT(ax1) * minor_step)
AX_TICK1(ax2,1) = wctick - (AX_INLEFT(ax2) * minor_step)
if (GL_SETAXISPOS(ap) == YES) {
AX_TICK1(ax1,2) = GL_AXISPOS1(ap)
AX_TICK1(ax2,2) = GL_AXISPOS2(ap)
} else {
AX_TICK1(ax1,2) = WCS_WY1(w)
AX_TICK1(ax2,2) = WCS_WY2(w)
}
AX_ISTEP(ax1,1) = minor_step
AX_ISTEP(ax2,1) = minor_step
char_height = ggetr (gp, "ch")
if (char_height < EPSILON)
char_height = DEF_CHARHEIGHT
char_height = char_height * GL_TICKLABELSIZE(ap)
AX_TICKLABELOFFSET(ax2,2) = 0.5 * char_height
AX_TICKLABELOFFSET(ax1,2) = -AX_TICKLABELOFFSET(ax2,2)
# Set gtext format for tick labels.
call strcpy ("hj=c,vj=t", AX_TICKLABELPOS(ax1), SZ_FORMAT)
call strcpy ("hj=c,vj=b", AX_TICKLABELPOS(ax2), SZ_FORMAT)
} else {
if (GL_SETAXISPOS(ap) == YES) {
AX_TICK1(ax1,1) = GL_AXISPOS1(ap)
AX_TICK1(ax2,1) = GL_AXISPOS2(ap)
} else {
AX_TICK1(ax1,1) = WCS_WX1(w)
AX_TICK1(ax2,1) = WCS_WX2(w)
}
AX_TICK1(ax1,2) = wctick - (AX_INLEFT(ax1) * minor_step)
AX_TICK1(ax2,2) = wctick - (AX_INLEFT(ax2) * minor_step)
AX_ISTEP(ax1,2) = minor_step
AX_ISTEP(ax2,2) = minor_step
char_width = ggetr (gp, "cw")
if (char_width < EPSILON)
char_width = DEF_CHARWIDTH
char_width = char_width * GL_TICKLABELSIZE(ap)
AX_TICKLABELOFFSET(ax2,1) = 0.5 * char_width
AX_TICKLABELOFFSET(ax1,1) = -AX_TICKLABELOFFSET(ax2,1)
call strcpy ("hj=r,vj=c", AX_TICKLABELPOS(ax1), SZ_FORMAT)
call strcpy ("hj=l,vj=c", AX_TICKLABELPOS(ax2), SZ_FORMAT)
}
# Set the tick parameters that are identical for the two axes and
# which do not depend on whether the axis is horizontal or vertical.
AX_DRAWTICKS(ax1) = GL_DRAWTICKS(ap)
AX_DRAWTICKS(ax2) = GL_DRAWTICKS(ap)
AX_TICKLABELSIZE(ax1) = GL_TICKLABELSIZE(ap)
AX_TICKLABELSIZE(ax2) = GL_TICKLABELSIZE(ap)
AX_TICKLABELCOLOR(ax1) = GL_TICKLABELCOLOR(ap)
AX_TICKLABELCOLOR(ax2) = GL_TICKLABELCOLOR(ap)
AX_TICKCOLOR(ax1) = GL_TICKCOLOR(ap)
AX_TICKCOLOR(ax2) = GL_TICKCOLOR(ap)
AX_GRIDCOLOR(ax1) = GL_GRIDCOLOR(ap)
AX_GRIDCOLOR(ax2) = GL_GRIDCOLOR(ap)
AX_AXISLABELSIZE(ax1) = GL_AXISLABELSIZE(ap)
AX_AXISLABELSIZE(ax2) = GL_AXISLABELSIZE(ap)
AX_AXISLABELCOLOR(ax1) = GL_AXISLABELCOLOR(ap)
AX_AXISLABELCOLOR(ax2) = GL_AXISLABELCOLOR(ap)
AX_AXISWIDTH(ax1) = GL_AXISWIDTH(ap)
AX_AXISWIDTH(ax2) = GL_AXISWIDTH(ap)
AX_AXISCOLOR(ax1) = GL_AXISCOLOR(ap)
AX_AXISCOLOR(ax2) = GL_AXISCOLOR(ap)
AX_MINORWIDTH(ax1) = GL_MINORWIDTH(ap)
AX_MINORWIDTH(ax2) = GL_MINORWIDTH(ap)
AX_MAJORWIDTH(ax1) = GL_MAJORWIDTH(ap)
AX_MAJORWIDTH(ax2) = GL_MAJORWIDTH(ap)
# Compute the number of digits of precision needed for the tick labels.
AX_NDIGITS(ax1) = max (1, gt_ndigits (p1, p2, step))
AX_NDIGITS(ax2) = AX_NDIGITS(ax1)
# If both axes are to be drawn label ticks if enabled. If only one
# axis is to be drawn that is the axis that must be labelled.
if (GL_DRAWAXES(ap) > 0) {
AX_LABELTICKS(ax1) = GL_LABELTICKS(ap)
AX_LABELTICKS(ax2) = GL_LABELTICKS(ap)
}
if (GL_DRAWAXES(ap) == 1 || GL_DRAWAXES(ap) == 3)
AX_LABELTICKS(ax2) = NO
else if (GL_DRAWAXES(ap) == 2)
AX_LABELTICKS(ax1) = NO
# The user may override the tick label format if desired.
if (GL_TICKFORMAT(ap) == EOS) {
call sprintf (AX_TICKFORMAT(ax1), SZ_FORMAT, "%%0.%dg")
call pargi (AX_NDIGITS(ax1) + 1)
} else
call strcpy (GL_TICKFORMAT(ap), AX_TICKFORMAT(ax1), SZ_FORMAT)
call strcpy (AX_TICKFORMAT(ax1), AX_TICKFORMAT(ax2), SZ_FORMAT)
end
# GLB_MINORSTEP -- Determine the step size for the minor ticks. Adapted
# from a routine by J. Eisenhamer (STScI) which was based on some MONGO code.
real procedure glb_minorstep (x1, x2, nminor)
real x1, x2 #I interval between major ticks
int nminor #I suggested number of minor ticks, or actual# if neg
int iexp
real amant, diff, num, range
begin
range = abs (x2 - x1)
if (nminor < 0)
return (range / real (-nminor + 1))
else {
# Determine magnitude of the intervals.
diff = log10 (range / nminor)
iexp = int (diff)
if (diff < 0)
iexp = iexp - 1
amant = diff - real(iexp)
# Determine an appropriate step size.
if (amant < 0.15)
num = 1.0
else if (amant < 0.50)
num = 2.0
else if (amant < 0.85)
num = 5.0
else
num = 10.0
return (num * 10.0**iexp)
}
end
|