Skip to content

Instantly share code, notes, and snippets.

@soypat
Last active June 10, 2025 17:25
Show Gist options
  • Save soypat/1253c460cac3a5e0c92862b879a02577 to your computer and use it in GitHub Desktop.
Save soypat/1253c460cac3a5e0c92862b879a02577 to your computer and use it in GitHub Desktop.
Generalized circle midpoint algorithm adapted for drawing multiple shapes
package main
import (
"image"
"image/color"
"image/png"
"os"
)
func main() {
red := color.RGBA{R: 255, A: 255}
img := image.NewRGBA(image.Rect(-100, -100, 100, 100))
r := 80
for i := 1; i < r; i++ {
Outline(img, i, red, nextTest)
}
// jeskoFill(img, r, red)
fp, _ := os.Create("circle.png")
defer fp.Close()
png.Encode(fp, img)
}
func nextCircle(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x--
}
return x, y, t1
}
func Outline(img *image.RGBA, r int, c color.RGBA, next func(x, y, t1 int) (xnext, ynext, t1next int)) {
t1 := r / 16 // Initialize for nicer looking shapes (circle case).
x := r
y := 0
for x >= y {
img.SetRGBA(x, y, c)
img.SetRGBA(x, -y, c)
img.SetRGBA(-x, y, c)
img.SetRGBA(-x, -y, c)
img.SetRGBA(y, x, c)
img.SetRGBA(y, -x, c)
img.SetRGBA(-y, x, c)
img.SetRGBA(-y, -x, c)
xprev := x
yprev := y
x, y, t1 = next(x, y, t1)
if xprev == x && yprev == y {
panic("unchanging x and y")
}
}
}
func Fill(img *image.RGBA, r int, c color.RGBA, next func(x, y, t1 int) (xnext, ynext, t1next int)) {
t1 := r / 16 // Initialize for nicer looking shapes (circle case).
x := r
y := 0
for x >= y {
for ix := -x; ix <= x; ix++ { // TODO: optimization: draw only when y value changes from greatest x found.
img.SetRGBA(ix, y, c)
img.SetRGBA(ix, -y, c)
img.SetRGBA(y, ix, c)
img.SetRGBA(-y, ix, c)
}
xprev := x
yprev := y
x, y, t1 = next(x, y, t1)
if xprev == x && yprev == y {
panic("unchanging x and y")
}
}
}
func nextTest(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y * x
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x -= y
}
return x, y, t1
}
func nextParabolaStar(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y * x
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x -= y
}
return x, y, t1
}
func next4Star(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y * x * 2
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x -= 2 * t2 / t1
}
return x, y, t1
}
func nextParabolaFractal(x, y, t1 int) (xnext, ynext, t1next int) {
y++
x *= 100
x /= 90
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x -= 1
}
return x, y, t1
}
func nextChamfered4Star(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2 + x
x -= 2
}
return x, y, t1
}
func nextClover(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2
x -= 2
}
return x, y, t1
}
func nextSquaredCircle(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2 - y
x--
}
return x, y, t1
}
func nextChamferedSquareDiamond(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x
if t2 >= 0 {
t1 = t2 + x
x--
}
return x, y, t1
}
func nextRoundedSquareDiamond(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1 - x/2
if t2 >= 0 {
t1 = t2
x--
}
return x, y, t1
}
func nextSquareDiamond(x, y, t1 int) (xnext, ynext, t1next int) {
y++
t1 += y
t2 := t1
if t2 >= 0 {
t1 = t2 + x
x--
}
return x, y, t1
}
func nextExpl(x, y, t1 int) (xnext, ynext, t1next int) {
ynext = y + 1
t1next = t1 + y
t2 := t1 - x
if t2 >= 0 {
t1next = t2
xnext = x + 1
} else {
xnext = x
}
return xnext, ynext, t1next
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment