### Better Ellipse Drawing Algorithm

master
parent 524ebebedb
commit 857e5ec098
3 changed files with 79 additions and 100 deletions
1. 64
ellipse.go
2. 8
functions.go
3. 107
shapes.go

#### 64 ellipse.go Unescape Escape View 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` `}`

#### 8 functions.go Unescape Escape View File

 `@ -98,3 +98,11 @@ func TrimBox(src, dst *Rect, p Point, S Rect, thickness int32) {` ` 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 Unescape Escape View File

 `@ -2,8 +2,6 @@ package render` ``` ``` `import (` ` "math"` ``` ``` ` "git.kirsle.net/apps/doodle/pkg/log"` `)` ``` ``` ```// 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` `}` ``` ``` ```// IterEllipse is a generator that draws out the pixels of an ellipse. ``` `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 ``` ```// IterEllipse iterates an Ellipse using two Points as the top-left and ``` ```// 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 (` ``` // xc = float32(A.X+B.X) / 2 ``` ``` // yc = float32(A.Y+B.Y) / 2 ``` ` xc = float32(B.X)` ` yc = float32(B.Y)` ` rx = float32(B.X - A.X)` ` ry = float32(B.Y - A.Y)` ` width = AbsInt32(B.X - A.X)` ` height = AbsInt32(B.Y - A.Y)` ` radius = NewPoint(width/2, height/2)` ` center = NewPoint(AbsInt32(B.X-radius.X), AbsInt32(B.Y-radius.Y))` ` )` ``` ``` ` if rx < 0 {` ` 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)` ` return MidpointEllipse(center, radius)` `}`