Better Ellipse Drawing Algorithm
This commit is contained in:
parent
524ebebedb
commit
857e5ec098
64
ellipse.go
Normal file
64
ellipse.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
// MidpointEllipse implements an ellipse plotting algorithm.
|
||||||
|
func MidpointEllipse(center, radius Point) chan Point {
|
||||||
|
yield := make(chan Point)
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
var (
|
||||||
|
pos = NewPoint(radius.X, 0)
|
||||||
|
delta = NewPoint(
|
||||||
|
2*radius.Y*radius.Y*pos.X,
|
||||||
|
2*radius.X*radius.X*pos.Y,
|
||||||
|
)
|
||||||
|
err = radius.X*radius.X -
|
||||||
|
radius.Y*radius.Y*radius.X +
|
||||||
|
(radius.Y*radius.Y)/4
|
||||||
|
)
|
||||||
|
|
||||||
|
for delta.Y < delta.X {
|
||||||
|
yield <- NewPoint(center.X+pos.X, center.Y+pos.Y)
|
||||||
|
yield <- NewPoint(center.X+pos.X, center.Y-pos.Y)
|
||||||
|
yield <- NewPoint(center.X-pos.X, center.Y+pos.Y)
|
||||||
|
yield <- NewPoint(center.X-pos.X, center.Y-pos.Y)
|
||||||
|
|
||||||
|
pos.Y++
|
||||||
|
|
||||||
|
if err < 0 {
|
||||||
|
delta.Y += 2 * radius.X * radius.X
|
||||||
|
err += delta.Y + radius.X*radius.X
|
||||||
|
} else {
|
||||||
|
pos.X--
|
||||||
|
delta.Y += 2 * radius.X * radius.X
|
||||||
|
delta.X -= 2 * radius.Y * radius.Y
|
||||||
|
err += delta.Y - delta.X + radius.X*radius.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = radius.X*radius.X*(pos.Y*pos.Y+pos.Y) +
|
||||||
|
radius.Y*radius.Y*(pos.X-1)*(pos.X-1) -
|
||||||
|
radius.Y*radius.Y*radius.X*radius.X
|
||||||
|
|
||||||
|
for pos.X >= 0 {
|
||||||
|
yield <- NewPoint(center.X+pos.X, center.Y+pos.Y)
|
||||||
|
yield <- NewPoint(center.X+pos.X, center.Y-pos.Y)
|
||||||
|
yield <- NewPoint(center.X-pos.X, center.Y+pos.Y)
|
||||||
|
yield <- NewPoint(center.X-pos.X, center.Y-pos.Y)
|
||||||
|
|
||||||
|
pos.X--
|
||||||
|
|
||||||
|
if err > 0 {
|
||||||
|
delta.X -= 2 * radius.Y * radius.Y
|
||||||
|
err += radius.Y*radius.Y - delta.X
|
||||||
|
} else {
|
||||||
|
pos.Y++
|
||||||
|
delta.Y += 2 * radius.X * radius.X
|
||||||
|
delta.X -= 2 * radius.Y * radius.Y
|
||||||
|
err += delta.Y - delta.X + radius.Y*radius.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(yield)
|
||||||
|
}()
|
||||||
|
return yield
|
||||||
|
}
|
|
@ -98,3 +98,11 @@ func TrimBox(src, dst *Rect, p Point, S Rect, thickness int32) {
|
||||||
dst.W = S.W - thickness
|
dst.W = S.W - thickness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AbsInt32 returns the absolute value of an int32.
|
||||||
|
func AbsInt32(v int32) int32 {
|
||||||
|
if v < 0 {
|
||||||
|
return -v
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
107
shapes.go
107
shapes.go
|
@ -2,8 +2,6 @@ package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IterLine is a generator that returns the X,Y coordinates to draw a line.
|
// IterLine is a generator that returns the X,Y coordinates to draw a line.
|
||||||
|
@ -93,106 +91,15 @@ func IterRect(p1, p2 Point) chan Point {
|
||||||
return generator
|
return generator
|
||||||
}
|
}
|
||||||
|
|
||||||
// IterEllipse is a generator that draws out the pixels of an ellipse.
|
// IterEllipse iterates an Ellipse using two Points as the top-left and
|
||||||
func IterEllipse(rx, ry, xc, yc float32) chan Point {
|
|
||||||
generator := make(chan Point)
|
|
||||||
|
|
||||||
mkPoint := func(x, y float32) Point {
|
|
||||||
return NewPoint(int32(x), int32(y))
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
var (
|
|
||||||
dx float32
|
|
||||||
dy float32
|
|
||||||
d1 float32
|
|
||||||
d2 float32
|
|
||||||
x float32
|
|
||||||
y = ry
|
|
||||||
)
|
|
||||||
|
|
||||||
d1 = (ry * ry) - (rx * rx * ry) + (0.25 * rx * rx)
|
|
||||||
dx = 2 * ry * ry * x
|
|
||||||
dy = 2 * rx * rx * y
|
|
||||||
|
|
||||||
// For region 1
|
|
||||||
for dx < dy {
|
|
||||||
// Yields points based on 4-way symmetry.
|
|
||||||
for _, point := range []Point{
|
|
||||||
mkPoint(x+xc, y+yc),
|
|
||||||
mkPoint(-x+xc, y+yc),
|
|
||||||
mkPoint(x+xc, -y+yc),
|
|
||||||
mkPoint(-x+xc, -y+yc),
|
|
||||||
} {
|
|
||||||
generator <- point
|
|
||||||
}
|
|
||||||
|
|
||||||
if d1 < 0 {
|
|
||||||
x++
|
|
||||||
dx = dx + (2 * ry * ry)
|
|
||||||
d1 = d1 + dx + (ry * ry)
|
|
||||||
} else {
|
|
||||||
x++
|
|
||||||
y--
|
|
||||||
dx = dx + (2 * ry * ry)
|
|
||||||
dy = dy - (2 * rx * rx)
|
|
||||||
d1 = d1 + dx - dy + (ry * ry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d2 = ((ry * ry) + ((x + 0.5) * (x + 0.5))) +
|
|
||||||
((rx * rx) * ((y - 1) * (y - 1))) -
|
|
||||||
(rx * rx * ry * ry)
|
|
||||||
|
|
||||||
// Region 2
|
|
||||||
for y >= 0 {
|
|
||||||
// Yields points based on 4-way symmetry.
|
|
||||||
for _, point := range []Point{
|
|
||||||
mkPoint(x+xc, y+yc),
|
|
||||||
mkPoint(-x+xc, y+yc),
|
|
||||||
mkPoint(x+xc, -y+yc),
|
|
||||||
mkPoint(-x+xc, -y+yc),
|
|
||||||
} {
|
|
||||||
generator <- point
|
|
||||||
}
|
|
||||||
|
|
||||||
if d2 > 0 {
|
|
||||||
y--
|
|
||||||
dy = dy - (2 * rx * rx)
|
|
||||||
d2 = d2 + (rx * rx) - dy
|
|
||||||
} else {
|
|
||||||
y--
|
|
||||||
x++
|
|
||||||
dx = dx + (2 * ry * ry)
|
|
||||||
dy = dy - (2 * rx * rx)
|
|
||||||
d2 = d2 + dx - dy + (rx * rx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(generator)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return generator
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterEllipse2 iterates an Ellipse using two Points as the top-left and
|
|
||||||
// bottom-right corners of a rectangle that encompasses the ellipse.
|
// bottom-right corners of a rectangle that encompasses the ellipse.
|
||||||
func IterEllipse2(A, B Point) chan Point {
|
func IterEllipse(A, B Point) chan Point {
|
||||||
var (
|
var (
|
||||||
// xc = float32(A.X+B.X) / 2
|
width = AbsInt32(B.X - A.X)
|
||||||
// yc = float32(A.Y+B.Y) / 2
|
height = AbsInt32(B.Y - A.Y)
|
||||||
xc = float32(B.X)
|
radius = NewPoint(width/2, height/2)
|
||||||
yc = float32(B.Y)
|
center = NewPoint(AbsInt32(B.X-radius.X), AbsInt32(B.Y-radius.Y))
|
||||||
rx = float32(B.X - A.X)
|
|
||||||
ry = float32(B.Y - A.Y)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if rx < 0 {
|
return MidpointEllipse(center, radius)
|
||||||
rx = -rx
|
|
||||||
}
|
|
||||||
if ry < 0 {
|
|
||||||
ry = -ry
|
|
||||||
}
|
|
||||||
log.Info("Ellipse btwn=%s-%s radius=%f,%f at center %f,%f", A, B, rx, ry, xc, yc)
|
|
||||||
return IterEllipse(rx, ry, xc, yc)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user