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  }