9fans.net/go@v0.0.5/draw/memdraw/ellipse.go (about) 1 // #include <u.h> 2 // #include <libc.h> 3 // #include <draw.h> 4 // #include <memdraw.h> 5 6 /* 7 * ellipse(dst, c, a, b, t, src, sp) 8 * draws an ellipse centered at c with semiaxes a,b>=0 9 * and semithickness t>=0, or filled if t<0. point sp 10 * in src maps to c in dst 11 * 12 * very thick skinny ellipses are brushed with circles (slow) 13 * others are approximated by filling between 2 ellipses 14 * criterion for very thick when b<a: t/b > 0.5*x/(1-x) 15 * where x = b/a 16 */ 17 18 package memdraw 19 20 import ( 21 "9fans.net/go/draw" 22 ) 23 24 type ellipseParam struct { 25 dst *Image 26 src *Image 27 c draw.Point 28 t int 29 sp draw.Point 30 disc *Image 31 op draw.Op 32 } 33 34 /* 35 * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 36 * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside 37 */ 38 39 type ellipseState struct { 40 a int 41 x int 42 a2 int64 43 b2 int64 44 b2x int64 45 a2y int64 46 c1 int64 47 c2 int64 48 ee int64 49 dxe int64 50 dye int64 51 d2xe int64 52 d2ye int64 53 } 54 55 func newstate(s *ellipseState, a int, b int) *ellipseState { 56 s.x = 0 57 s.a = a 58 s.a2 = int64(a * a) 59 s.b2 = int64(b * b) 60 s.b2x = int64(0) 61 s.a2y = s.a2 * int64(b) 62 s.c1 = -((s.a2 >> 2) + int64(a&1) + s.b2) 63 s.c2 = -((s.b2 >> 2) + int64(b&1)) 64 s.ee = -s.a2y 65 s.dxe = int64(0) 66 s.dye = s.ee << 1 67 s.d2xe = s.b2 << 1 68 s.d2ye = s.a2 << 1 69 return s 70 } 71 72 /* 73 * return x coord of rightmost pixel on next scan line 74 */ 75 func step(s *ellipseState) int { 76 for s.x < s.a { 77 if s.ee+s.b2x <= s.c1 || s.ee+s.a2y <= s.c2 { /* e(x+1,y-1/2) <= 0 */ /* e(x+1/2,y) <= 0 (rare) */ 78 s.dxe += s.d2xe 79 s.ee += s.dxe 80 s.b2x += s.b2 81 s.x++ 82 continue 83 } 84 s.dye += s.d2ye 85 s.ee += s.dye 86 s.a2y -= s.a2 87 if s.ee-s.a2y <= s.c2 { /* e(x+1/2,y-1) <= 0 */ 88 s.dxe += s.d2xe 89 s.ee += s.dxe 90 s.b2x += s.b2 91 tmp21 := s.x 92 s.x++ 93 return tmp21 94 } 95 break 96 } 97 return s.x 98 } 99 100 func Ellipse(dst *Image, c draw.Point, a int, b int, t int, src *Image, sp draw.Point, op draw.Op) { 101 if a < 0 { 102 a = -a 103 } 104 if b < 0 { 105 b = -b 106 } 107 var p ellipseParam 108 p.dst = dst 109 p.src = src 110 p.c = c 111 p.t = t 112 p.sp = sp.Sub(c) 113 p.disc = nil 114 p.op = op 115 116 u := (t << 1) * (a - b) 117 var in ellipseState 118 if b < a && u > b*b || a < b && -u > a*a { 119 /* if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*a/b) # very thick */ 120 bellipse(b, newstate(&in, a, b), &p) 121 return 122 } 123 var out ellipseState 124 var y int 125 var inb int 126 127 if t < 0 { 128 inb = -1 129 y = b 130 newstate(&out, a, y) 131 } else { 132 inb = b - t 133 y = b + t 134 newstate(&out, a+t, y) 135 } 136 if t > 0 { 137 newstate(&in, a-t, inb) 138 } 139 inx := 0 140 for ; y >= 0; y-- { 141 outx := step(&out) 142 if y > inb { 143 erect(-outx, y, outx, y, &p) 144 if y != 0 { 145 erect(-outx, -y, outx, -y, &p) 146 } 147 continue 148 } 149 if t > 0 { 150 inx = step(&in) 151 if y == inb { 152 inx = 0 153 } 154 } else if inx > outx { 155 inx = outx 156 } 157 erect(inx, y, outx, y, &p) 158 if y != 0 { 159 erect(inx, -y, outx, -y, &p) 160 } 161 erect(-outx, y, -inx, y, &p) 162 if y != 0 { 163 erect(-outx, -y, -inx, -y, &p) 164 } 165 inx = outx + 1 166 } 167 } 168 169 /* 170 * a brushed ellipse 171 */ 172 func bellipse(y int, s *ellipseState, p *ellipseParam) { 173 t := p.t 174 var err error 175 p.disc, err = AllocImage(draw.Rect(-t, -t, t+1, t+1), draw.GREY1) 176 if err != nil { 177 return 178 } 179 FillColor(p.disc, draw.Transparent) 180 Ellipse(p.disc, draw.ZP, t, t, -1, Opaque, draw.ZP, p.op) 181 oy := y 182 ox := 0 183 x := step(s) 184 nx := x 185 for { 186 for nx == x { 187 nx = step(s) 188 } 189 y++ 190 eline(-x, -oy, -ox, -y, p) 191 eline(ox, -oy, x, -y, p) 192 eline(-x, y, -ox, oy, p) 193 eline(ox, y, x, oy, p) 194 ox = x + 1 195 x = nx 196 y-- 197 oy = y 198 if oy <= 0 { 199 break 200 } 201 } 202 } 203 204 /* 205 * a rectangle with closed (not half-open) coordinates expressed 206 * relative to the center of the ellipse 207 */ 208 func erect(x0 int, y0 int, x1 int, y1 int, p *ellipseParam) { 209 /* print("R %d,%d %d,%d\n", x0, y0, x1, y1); */ 210 r := draw.Rect(p.c.X+x0, p.c.Y+y0, p.c.X+x1+1, p.c.Y+y1+1) 211 Draw(p.dst, r, p.src, p.sp.Add(r.Min), Opaque, draw.ZP, p.op) 212 } 213 214 /* 215 * a brushed point similarly specified 216 */ 217 func epoint(x int, y int, p *ellipseParam) { 218 /* print("P%d %d,%d\n", p->t, x, y); */ 219 p0 := draw.Pt(p.c.X+x, p.c.Y+y) 220 r := draw.Rpt(p0.Add(p.disc.R.Min), p0.Add(p.disc.R.Max)) 221 Draw(p.dst, r, p.src, p.sp.Add(r.Min), p.disc, p.disc.R.Min, p.op) 222 } 223 224 /* 225 * a brushed horizontal or vertical line similarly specified 226 */ 227 func eline(x0 int, y0 int, x1 int, y1 int, p *ellipseParam) { 228 /* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); */ 229 if x1 > x0+1 { 230 erect(x0+1, y0-p.t, x1-1, y1+p.t, p) 231 } else if y1 > y0+1 { 232 erect(x0-p.t, y0+1, x1+p.t, y1-1, p) 233 } 234 epoint(x0, y0, p) 235 if x1-x0 != 0 || y1-y0 != 0 { 236 epoint(x1, y1, p) 237 } 238 }