Bresenham's algorithm for bezier curves
This commit is contained in:
parent
f2b5b5081f
commit
2ab8747b4a
|
@ -0,0 +1,333 @@
|
|||
# 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
|
||||
}
|
||||
set-cursor-position 0/screen, 0/x, 0x1d/y
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, sx, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, sy, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, xx, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, yy, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, xy, 4/fg 0/bg
|
||||
# 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-<=
|
||||
set-cursor-position 0/screen, 0/x 0x1e/y
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "swapping P0 and P2", 4/fg 0/bg
|
||||
# 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
|
||||
set-cursor-position 0/screen, 0/x 0x1f/y
|
||||
#? printf("sx %d sy %d xx %ld yy %ld xy %ld cur %g\n", sx, sy, xx, yy, xy, cur);
|
||||
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
|
||||
# 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
|
||||
# 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
|
||||
}
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, sx, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, sy, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, xx, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, yy, 4/fg 0/bg
|
||||
draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 4/fg 0/bg
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, xy, 4/fg 0/bg
|
||||
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
|
||||
#
|
||||
#? set-cursor-position 0, 0/x 0/y
|
||||
#? var screen-y/esi: int <- copy 0
|
||||
$draw-monotonic-bezier:loop: {
|
||||
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg 0/bg
|
||||
#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 3/fg 0/bg
|
||||
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg 0/bg
|
||||
#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " vs ", 3/fg 0/bg
|
||||
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x2, 3/fg 0/bg
|
||||
#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 3/fg 0/bg
|
||||
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y2, 3/fg 0/bg
|
||||
#? var dummy/eax: int <- render-float-decimal 0/screen, dx-f, 3/precision, 0/x screen-y, 3/fg 0/bg
|
||||
#? var dummy/eax: int <- render-float-decimal 0/screen, dy-f, 3/precision, 0x10/x screen-y, 3/fg 0/bg
|
||||
#? var dummy/eax: int <- render-float-decimal 0/screen, err-f, 3/precision, 0x20/x screen-y, 3/fg 0/bg
|
||||
#? move-cursor-to-left-margin-of-next-line 0/screen
|
||||
#? screen-y <- increment
|
||||
pixel screen, x, y, color
|
||||
#? {
|
||||
#? var foo/eax: byte <- read-key 0/keyboard
|
||||
#? compare foo, 0
|
||||
#? loop-if-=
|
||||
#? }
|
||||
# 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<=
|
||||
#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "x step", 3/fg 0/bg
|
||||
#? move-cursor-to-left-margin-of-next-line 0/screen
|
||||
# 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-=
|
||||
#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "y step", 3/fg 0/bg
|
||||
#? move-cursor-to-left-margin-of-next-line 0/screen
|
||||
# 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,0 +1,46 @@
|
|||
#include<assert.h>
|
||||
#include<stdio.h>
|
||||
|
||||
void setPixel(int x, int y) {
|
||||
printf("%d %d\n", x, y);
|
||||
}
|
||||
|
||||
void plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int sx = x2-x1, sy = y2-y1;
|
||||
long xx = x0-x1, yy = y0-y1, xy; /* relative values for checks */
|
||||
double dx, dy, err, cur = xx*sy-yy*sx; /* curvature */
|
||||
|
||||
assert(xx*sx <= 0 && yy*sy <= 0); /* sign of gradient must not change */
|
||||
|
||||
printf("0 sx %d sy %d xx %ld yy %ld xy %ld cur %g\n", sx, sy, xx, yy, xy, cur);
|
||||
if (sx*(long)sx+sy*(long)sy > xx*xx+yy*yy) { /* begin with longer part */
|
||||
printf("swap\n");
|
||||
x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur; /* swap P0 P2 */
|
||||
}
|
||||
if (cur != 0) { /* no straight line */
|
||||
xx += sx; xx *= sx = x0 < x2 ? 1 : -1; /* x step direction */
|
||||
yy += sy; yy *= sy = y0 < y2 ? 1 : -1; /* y step direction */
|
||||
xy = 2*xx*yy; xx *= xx; yy *= yy; /* differences 2nd degree */
|
||||
if (cur*sx*sy < 0) { /* negated curvature? */
|
||||
xx = -xx; yy = -yy; xy = -xy; cur = -cur;
|
||||
}
|
||||
printf("1 sx %d sy %d xx %ld yy %ld xy %ld cur %g\n", sx, sy, xx, yy, xy, cur);
|
||||
dx = 4.0*sy*cur*(x1-x0)+xx-xy; /* differences 1st degree */
|
||||
dy = 4.0*sx*cur*(y0-y1)+yy-xy;
|
||||
xx += xx; yy += yy; err = dx+dy+xy; /* error 1st step */
|
||||
do {
|
||||
printf("%d %d: dx %g dy %g err %g\n", x0, y0, dx, dy, err);
|
||||
if (x0 == x2 && y0 == y2) return; /* last pixel -> curve finished */
|
||||
y1 = 2*err < dx; /* save value for test of y step */
|
||||
if (2*err > dy) { x0 += sx; dx -= xy; err += dy += yy; } /* x step */
|
||||
if ( y1 ) { y0 += sy; dy -= xy; err += dx += xx; } /* y step */
|
||||
} while (dy < dx ); /* gradient negates -> algorithm fails */
|
||||
}
|
||||
//? plotLine(x0,y0, x2,y2); /* plot remaining part to end */
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
plotQuadBezierSeg(1, 1, 0x80, 0x100, 0x200, 0x140);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
draw-line screen, 1 1, 0x80 0x100, 7/color
|
||||
draw-line screen, 0x80 0x100, 0x200 0x140, 7/color
|
||||
draw-monotonic-bezier screen, 1 1, 0x80 0x100, 0x200 0x140, 0xc/color
|
||||
draw-disc screen, 1 1, 3, 7/color 0xf/border
|
||||
draw-disc screen, 0x80 0x100, 3, 7/color 0xf/border
|
||||
draw-disc screen, 0x200 0x140, 3, 7/color 0xf/border
|
||||
}
|
Loading…
Reference in New Issue