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
|
# Draw a second-degree bezier curve using 3 control points.
#
# http://members.chello.at/easyfilter/bresenham.html says that this algorithm
# works only if "the gradient does not change sign". Either:
# x0 >= x1 >= x2
# or:
# x0 <= x1 <= x2
# Similarly for y0, y1 and y2.
#
# This seems superficially similar to the notions of convex and concave, but I
# think it isn't. I think it's purely a property of the frame of reference.
# Rotating the axes can make the gradient change sign or stop changing sign
# even as 3 points preserve fixed relative bearings to each other.
fn draw-monotonic-bezier screen: (addr screen), x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int {
var xx: int
var yy: int
var xy: int
var sx: int
var sy: int
# sx = x2-x1
var tmp/eax: int <- copy x2
tmp <- subtract x1
copy-to sx, tmp
# sy = y2-y1
tmp <- copy y2
tmp <- subtract y1
copy-to sy, tmp
# xx = x0-x1
tmp <- copy x0
tmp <- subtract x1
copy-to xx, tmp
# yy = y0-y1
tmp <- copy y0
tmp <- subtract y1
copy-to yy, tmp
# cur = xx*sy - yy*sx
var cur-f/xmm4: float <- convert xx
{
var sy-f/xmm1: float <- convert sy
cur-f <- multiply sy-f
var tmp2-f/xmm1: float <- convert yy
var sx-f/xmm2: float <- convert sx
tmp2-f <- multiply sx-f
cur-f <- subtract tmp2-f
}
# if (xx*sx > 0) abort
{
tmp <- copy xx
tmp <- multiply sx
compare tmp, 0
break-if-<=
abort "bezier: gradient of x changes sign"
}
# if (yy*sy > 0) abort
{
tmp <- copy yy
tmp <- multiply sy
compare tmp, 0
break-if-<=
abort "bezier: gradient of y changes sign"
}
# swap P0 and P2 if necessary
{
# dist1 = sx*sx + sy*sy
var dist1/ecx: int <- copy sx
{
dist1 <- multiply sx
{
break-if-not-overflow
abort "bezier: overflow 1"
}
tmp <- copy sy
tmp <- multiply sy
{
break-if-not-overflow
abort "bezier: overflow 2"
}
dist1 <- add tmp
}
# dist2 = xx*xx + yy*yy
var dist2/edx: int <- copy xx
{
dist2 <- multiply xx
{
break-if-not-overflow
abort "bezier: overflow 3"
}
tmp <- copy yy
tmp <- multiply yy
{
break-if-not-overflow
abort "bezier: overflow 4"
}
dist2 <- add tmp
}
# if (dist1 <= dist2) break
compare dist1, dist2
break-if-<=
# swap x0 and x2
tmp <- copy x0
copy-to x2, tmp
tmp <- copy sx
tmp <- add x1
copy-to x0, tmp
# swap y0 and y2
tmp <- copy y0
copy-to y2, tmp
tmp <- copy sy
tmp <- add y1
copy-to y0, tmp
# cur = -cur
var negative-1/eax: int <- copy -1
var negative-1-f/xmm1: float <- convert negative-1
cur-f <- multiply negative-1-f
}
var x/ecx: int <- copy x0
var y/edx: int <- copy y0
var zero-f: float
# plot a curved part if necessary
$draw-monotonic-bezier:curve: {
compare cur-f, zero-f
break-if-=
# xx += sx
tmp <- copy sx
add-to xx, tmp
# sx = sgn(x2-x)
tmp <- copy x2
tmp <- subtract x
tmp <- sgn tmp
copy-to sx, tmp
# xx *= sx
tmp <- copy sx
tmp <- multiply xx
copy-to xx, tmp
# yy += sy
tmp <- copy sy
add-to yy, tmp
# sy = sgn(y2-y)
tmp <- copy y2
tmp <- subtract y
tmp <- sgn tmp
copy-to sy, tmp
# yy *= sy
tmp <- copy sy
tmp <- multiply yy
copy-to yy, tmp
# xy = 2*xx*xy
tmp <- copy xx
tmp <- multiply yy
{
break-if-not-overflow
abort "bezier: overflow 5"
}
tmp <- shift-left 1
{
break-if-not-overflow
abort "bezier: overflow 6"
}
copy-to xy, tmp
# xx *= xx
tmp <- copy xx
tmp <- multiply tmp
{
break-if-not-overflow
abort "bezier: overflow 7"
}
copy-to xx, tmp
# yy *= yy
tmp <- copy yy
tmp <- multiply tmp
{
break-if-not-overflow
abort "bezier: overflow 7"
}
copy-to yy, tmp
# if (cur*sx*sy < 0) negative curvature
{
var tmp-f/xmm0: float <- copy cur-f
var sx-f/xmm1: float <- convert sx
tmp-f <- multiply sx-f
var sy-f/xmm1: float <- convert sy
tmp-f <- multiply sy-f
compare tmp-f, zero-f
break-if-float>=
#
negate xx
negate yy
negate xy
# cur = -cur
var negative-1/eax: int <- copy -1
var negative-1-f/xmm1: float <- convert negative-1
cur-f <- multiply negative-1-f
}
var four/ebx: int <- copy 4
var dx-f/xmm5: float <- convert four
var dy-f/xmm6: float <- convert four
# dx = 4*sy*cur*(x1-x0) + xx - xy
{
var tmp/xmm0: float <- convert sy
dx-f <- multiply tmp
dx-f <- multiply cur-f
tmp <- convert x1
var tmp2/xmm3: float <- convert x
tmp <- subtract tmp2
dx-f <- multiply tmp
tmp <- convert xx
dx-f <- add tmp
tmp <- convert xy
dx-f <- subtract tmp
}
# dy-f = 4*sx*cur*(y0-y1) + yy - xy
{
var tmp/xmm0: float <- convert sx
dy-f <- multiply tmp
dy-f <- multiply cur-f
tmp <- convert y
var tmp2/xmm3: float <- convert y1
tmp <- subtract tmp2
dy-f <- multiply tmp
tmp <- convert yy
dy-f <- add tmp
tmp <- convert xy
dy-f <- subtract tmp
}
# xx += xx
tmp <- copy xx
add-to xx, tmp
# yy += yy
tmp <- copy yy
add-to yy, tmp
# err = dx+dy+xy
var err-f/xmm7: float <- copy dx-f
err-f <- add dy-f
var xy-f/xmm0: float <- convert xy
err-f <- add xy-f
#
$draw-monotonic-bezier:loop: {
pixel screen, x, y, color
# if (x == x2 && y == y2) return
{
compare x, x2
break-if-!=
compare y, y2
break-if-!=
return
}
# perform-y-step? = (2*err < dx)
var perform-y-step?/eax: boolean <- copy 0/false
var two-err-f/xmm0: float <- copy err-f
{
var two/ebx: int <- copy 2
var two-f/xmm1: float <- convert two
two-err-f <- multiply two-f
compare two-err-f, dx-f
break-if-float>=
perform-y-step? <- copy 1/true
}
# if (2*err > dy)
{
compare two-err-f, dy-f
break-if-float<=
# x += sx
x <- add sx
# dx -= xy
var xy-f/xmm0: float <- convert xy
dx-f <- subtract xy-f
# dy += yy
var yy-f/xmm0: float <- convert yy
dy-f <- add yy-f
# err += dy
err-f <- add dy-f
}
# if perform-y-step?
{
compare perform-y-step?, 0/false
break-if-=
# y += sy
y <- add sy
# dy -= xy
var xy-f/xmm0: float <- convert xy
dy-f <- subtract xy-f
# dx += xx
var xx-f/xmm0: float <- convert xx
dx-f <- add xx-f
# err += dx
err-f <- add dx-f
}
# if (dy < dx) loop
compare dy-f, dx-f
loop-if-float<
}
}
# plot the remaining straight line
draw-line screen, x y, x2 y2, color
}
# 0 <= u <= 1
fn bezier-point u: float, x0: int, x1: int, x2: int -> _/eax: int {
var one/eax: int <- copy 1
var u-prime/xmm0: float <- convert one
u-prime <- subtract u
var result/xmm1: float <- convert x0
result <- multiply u-prime
result <- multiply u-prime
var term2/xmm2: float <- convert x1
term2 <- multiply u
term2 <- multiply u-prime
result <- add term2
result <- add term2
var term3/xmm2: float <- convert x2
term3 <- multiply u
term3 <- multiply u
result <- add term3
var result/eax: int <- convert result
return result
}
|