Last active
June 10, 2025 17:25
-
-
Save soypat/1253c460cac3a5e0c92862b879a02577 to your computer and use it in GitHub Desktop.
Generalized circle midpoint algorithm adapted for drawing multiple shapes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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