You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
7.5 KiB
316 lines
7.5 KiB
# 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 |
|
}
|
|
|