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
|
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
include <gki.h>
include <gio.h>
define MOVE 0
define DRAW 1
# GADRAW -- Draw absolute. This is the primary line drawing primitive, used
# to transform and clip polylines, polymarkers, and polygons (fill area).
# Our function is to handle INDEFS, the normalization transformation, and
# clipping, building up a polyline in GKI coordinates. Each call processes
# a point of the input polyline, adding zero, one, or two points to the output
# clipped polyline, which is buffered internally in the static polyline buffer
# PL. Plotting an INDEF terminates the polyline and starts a new one, causing
# a gap to appear in the plotted polyline. Long polylines are broken up into
# shorter polylines to simplify buffering. The transformation parameters are
# computed and cached in the GPL common for maximum efficiency.
procedure gadraw (gp, wx, wy)
pointer gp # graphics descriptor
real wx, wy # absolute world coordinates of next point
int i
real x, y
long mx, my
bool inbounds
include "gpl.com"
begin
# Update cached transformation parameters if device changes or cache
# is invalidated by setting gp_out to null. If the WCS changes it
# is not necessary to flush the polyline but we must update the
# cached transformation parameters.
if (gp != gp_out) {
call gpl_flush()
call gpl_cache (gp)
} else if (GP_WCS(gp) != wcs)
call gpl_cache (gp)
# Break polyline (visible break in the plotted line) if point is
# indefinite.
if (IS_INDEFR(wx) || IS_INDEFR(wy)) {
call gamove (gp, wx, wy)
return
}
# Transform point (wx,wy) to long integer NDC coordinates in the range
# 0 to GKI_MAXNDC. This combines the WCS->NDC->GKI transformations into
# a single transformation and permits use of integer arithmetic for
# clipping. Long integer arithmetic is necessary to provide sufficient
# precision to represent GKI_MAXNDC**2, the largest possible integer
# value in an expression.
if (xtran == LINEAR && ytran == LINEAR) {
# Optimize the case linear.
x = max (0.0, min (real(GKI_MAXNDC),
((wx - wxorigin) * xscale) + mxorigin))
y = max (0.0, min (real(GKI_MAXNDC),
((wy - wyorigin) * yscale) + myorigin))
} else {
# General case.
call gpl_wcstogki (gp, wx, wy, x, y)
}
# Check to see if this is the first point of a new polyline. If so we
# must set the first physical point in the output polyline to the
# current position, making the current point the second physical point
# of the output polyline. If the current position is indefinite
# then we take the current point to define the current position and
# it is put into the polyline on the next call.
if (op == 1) {
if (IS_INDEF(cx) || IS_INDEF(cy)) {
cx = x
cy = y
return
} else {
# Place the current pen position in the polyline as the
# first point if it is inbounds.
mx = cx
my = cy
if (my <= my2 && my >= my1 && mx <= mx2 && mx >= mx1) {
last_point_inbounds = true
pl[op] = mx
op = op + 1
pl[op] = my
op = op + 1
} else {
last_point_inbounds = false
do i = 1, 4 {
xs[i] = cx
ys[i] = cy
}
}
}
}
# Update the current position, maintained in GKI coordinates to make
# the current position invariant with respect to changes in the
# current WCS. The current position is maintained in floating point
# to minimize the accumulation of errors in relative moves and draws.
cx = x
cy = y
# Convert to long integer metacode coords for clipping.
mx = x
my = y
# Clip at either the viewport boundary or the edge of the device screen,
# if clipping is "disabled". Clipping is performed in NDC space rather
# than world space because NDC space is simpler (mx1 < mx2, my1 < my2,
# no log scaling), and because we need to clip at the device screen
# boundary anyhow. If the boundary is crossed the polyline is broken.
# A line segment may lie entirely outside the viewport, entirely inside,
# may cross from inside to outside, from outside to inside, or may
# cross twice (cross two different boundaries). The clipping algorithm
# used (Harrington, 1983; Sutherland and Hodgman, 1974) clips at each
# of the four boundaries in sequence, using the clipped point from the
# previous iteration as input to the next. It isn't simple but neither
# is the problem. The code is optimized for the usual inbounds case.
# Clipped points are discarded.
inbounds = (my <= my2 && my >= my1 && mx <= mx2 && mx >= mx1)
if (inbounds && (last_point_inbounds || pl_pointmode == YES)) {
# Add point to polyline (the fast way).
pl[op] = mx
op = op + 1
pl[op] = my
op = op + 1
} else if (pl_pointmode == NO) {
if (last_point_inbounds) {
# Update coords of last point drawn (necessary since we did
# not use the clipping code for inbounds points).
do i = 1, 4 {
xs[i] = pl[op-2]
ys[i] = pl[op-1]
}
}
call gpl_clipl (DRAW, mx, my)
}
last_point_inbounds = inbounds
# Break long polylines to avoid overflowing the polyline output
# buffer. The output buffer contains two cells for each output
# point (x,y pair). There must be space for at least two points
# (four cells) left in the buffer, since a single clip operation
# can add up to two points to the polyline. OP points to the next
# available cell.
if (op > LEN_PLBUF - 2)
call gpl_flush()
end
# GPL_CLIPL -- Clip at left boundary.
procedure gpl_clipl (pen, mx, my)
int pen # move or draw
long mx, my # point to be clipped
int newpen
include "gpl.com"
begin
# Does line cross boundary?
if ((mx >= mx1 && xs[1] < mx1) || (mx <= mx1 && xs[1] > mx1)) {
if (mx >= mx1)
newpen = MOVE
else
newpen = pen
call gpl_clipr (newpen, mx1,
(my - ys[1]) * (mx1 - mx) / (mx - xs[1]) + my)
}
xs[1] = mx
ys[1] = my
if (mx >= mx1)
call gpl_clipr (pen, mx, my)
end
# GPL_CLIPR -- Clip at right boundary.
procedure gpl_clipr (pen, mx, my)
int pen # move or draw
long mx, my # point to be clipped
int newpen
include "gpl.com"
begin
# Does line cross boundary?
if ((mx <= mx2 && xs[2] > mx2) || (mx >= mx2 && xs[2] < mx2)) {
if (mx <= mx2)
newpen = MOVE
else
newpen = pen
call gpl_clipb (newpen, mx2,
(my - ys[2]) * (mx2 - mx) / (mx - xs[2]) + my)
}
xs[2] = mx
ys[2] = my
if (mx <= mx2)
call gpl_clipb (pen, mx, my)
end
# GPL_CLIPB -- Clip at bottom boundary.
procedure gpl_clipb (pen, mx, my)
int pen # move or draw
long mx, my # point to be clipped
int newpen
include "gpl.com"
begin
# Does line cross boundary?
if ((my >= my1 && ys[3] < my1) || (my <= my1 && ys[3] > my1)) {
if (my >= my1)
newpen = MOVE
else
newpen = pen
call gpl_clipt (newpen,
(mx - xs[3]) * (my1 - my) / (my - ys[3]) + mx, my1)
}
xs[3] = mx
ys[3] = my
if (my >= my1)
call gpl_clipt (pen, mx, my)
end
# GPL_CLIPT -- Clip at top boundary and put the final clipped point(s) in
# the output polyline. Note that a "move" at this level does not affect
# the current position (cx,cy), since the vector endpoints have been clipped
# and the current position vector follows the unclipped vector points input
# by the user.
procedure gpl_clipt (pen, mx, my)
int pen # move or draw
long mx, my # point to be clipped
include "gpl.com"
begin
# Does line cross boundary?
if ((my <= my2 && ys[4] > my2) || (my >= my2 && ys[4] < my2)) {
if (my <= my2 || pen == MOVE)
call gpl_flush()
pl[op] = (mx - xs[4]) * (my2 - my) / (my - ys[4]) + mx
op = op + 1
pl[op] = my2
op = op + 1
}
xs[4] = mx
ys[4] = my
if (my <= my2) {
if (pen == MOVE)
call gpl_flush()
pl[op] = mx
op = op + 1
pl[op] = my
op = op + 1
}
end
|