/
circle.go
146 lines (135 loc) · 3.39 KB
/
circle.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Draw circles using the Bresenham Circle Algorithm
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"image"
"image/color"
"image/png"
"io"
"log"
"math"
"net/http"
"os"
"strconv"
)
const defaultRadius = 3
func main() {
radius := flag.Int("r", defaultRadius, "radius of circle")
httpAddr := flag.String("http", "", "run HTTP server on `address` (e.g., :8080) to serve circle images\nexample URL: http://localhost:8080?r=42")
flag.Parse()
if *httpAddr != "" {
http.HandleFunc("/", httpHandler)
log.Printf("listening on %s", *httpAddr)
log.Fatal(http.ListenAndServe(*httpAddr, nil))
} else {
writer := bufio.NewWriter(os.Stdout)
_ = drawCircleText(*radius, writer)
_ = writer.Flush()
}
}
func httpHandler(w http.ResponseWriter, r *http.Request) {
radius := defaultRadius
radiusStr := r.FormValue("r")
if radiusStr != "" {
var err error
radius, err = strconv.Atoi(radiusStr)
if err != nil || radius < 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, `radius must be a positive integer, e.g., ?r=42`)
return
}
}
log.Printf("drawing circle of radius %d", radius)
err := drawCircleImage(radius, w)
if err != nil {
fmt.Fprintf(w, "error encoding image: %s", err)
return
}
}
// Draw circle of radius r and write to writer as PNG.
func drawCircleImage(r int, writer io.Writer) error {
// Encode image as PNG with white pixels for circle
size := r*2 + 1
im := image.NewNRGBA(image.Rect(0, 0, size, size))
drawCircleInt(r, func(x, y int) {
im.SetNRGBA(x+r, r-y, color.NRGBA{255, 255, 255, 255})
})
return png.Encode(writer, im)
}
// Draw circle of radius r and write to writer as text.
func drawCircleText(r int, writer io.Writer) error {
// Create 2-D "screen buffer" to hold pixels, but include room
// for a newline after each line. For example, with r=3 showing,
// the top-right quadrant drawn (newlines are denoted by 'N'):
// ...##..N
// ...|.#.N
// ...|..#N
// ---+--#N
// ...|...N
// ...|...N
// ...|...N
size := r*2 + 1
buf := bytes.Repeat([]byte{' '}, (size+1)*size)
for i := size; i < len(buf); i += size + 1 {
buf[i] = '\n'
}
drawCircleInt(r, func(x, y int) {
buf[(size+1)*(r-y)+x+r] = '#'
})
_, err := writer.Write(buf)
return err
}
// Draw circle of radius r using given putPixel function; use simple
// square root method.
func drawCircleSqrt(r int, putPixel func(x, y int)) {
y := r
rsq := r * r
for x := 0; x <= y; x++ {
// Just calculate y = sqrt(r^2 - x^2)
y := int(math.Round(math.Sqrt(float64(rsq - x*x))))
putPixel(x, y)
putPixel(y, x)
putPixel(-x, y)
putPixel(-y, x)
putPixel(x, -y)
putPixel(y, -x)
putPixel(-x, -y)
putPixel(-y, -x)
}
}
// Draw circle of radius r using given putPixel function; use
// Bresenham-ish method with no sqrt and only integer math.
func drawCircleInt(r int, putPixel func(x, y int)) {
x := 0
y := r
xsq := 0
rsq := r * r
ysq := rsq
// Loop x from 0 to the line x==y. Start y at r and each time
// around the loop either keep it the same or decrement it.
for x <= y {
putPixel(x, y)
putPixel(y, x)
putPixel(-x, y)
putPixel(-y, x)
putPixel(x, -y)
putPixel(y, -x)
putPixel(-x, -y)
putPixel(-y, -x)
// New x^2 = (x+1)^2 = x^2 + 2x + 1
xsq = xsq + 2*x + 1
x++
// Potential new y^2 = (y-1)^2 = y^2 - 2y + 1
y1sq := ysq - 2*y + 1
// Choose y or y-1, whichever gives smallest error
a := xsq + ysq
b := xsq + y1sq
if a-rsq >= rsq-b {
y--
ysq = y1sq
}
}
}