9fans.net/go@v0.0.5/draw/bezier.go (about)

     1  package draw
     2  
     3  func appendpt(l *[]Point, p Point) {
     4  	*l = append(*l, p)
     5  }
     6  
     7  func normsq(p Point) int {
     8  	return p.X*p.X + p.Y*p.Y
     9  }
    10  
    11  func psdist(p, a, b Point) int {
    12  	p = p.Sub(a)
    13  	b = b.Sub(a)
    14  	num := p.X*b.X + p.Y*b.Y
    15  	if num <= 0 {
    16  		return normsq(p)
    17  	}
    18  	den := normsq(b)
    19  	if num >= den {
    20  		return normsq(b.Sub(p))
    21  	}
    22  	return normsq(b.Mul(num).Div(den).Sub(p))
    23  }
    24  
    25  /*
    26   * Convert cubic Bezier curve control points to polyline
    27   * vertices.  Leaves the last vertex off, so you can continue
    28   * with another curve.
    29   */
    30  func bpts1(l *[]Point, p0, p1, p2, p3 Point, scale int) {
    31  	tp0 := p0.Div(scale)
    32  	tp1 := p1.Div(scale)
    33  	tp2 := p2.Div(scale)
    34  	tp3 := p3.Div(scale)
    35  	if psdist(tp1, tp0, tp3) <= 1 && psdist(tp2, tp0, tp3) <= 1 {
    36  		appendpt(l, tp0)
    37  		appendpt(l, tp1)
    38  		appendpt(l, tp2)
    39  	} else {
    40  		/*
    41  		 * if scale factor is getting too big for comfort,
    42  		 * rescale now & concede the rounding error
    43  		 */
    44  		if scale > 1<<12 {
    45  			p0 = tp0
    46  			p1 = tp1
    47  			p2 = tp2
    48  			p3 = tp3
    49  			scale = 1
    50  		}
    51  		p01 := p0.Add(p1)
    52  		p12 := p1.Add(p2)
    53  		p23 := p2.Add(p3)
    54  		p012 := p01.Add(p12)
    55  		p123 := p12.Add(p23)
    56  		p0123 := p012.Add(p123)
    57  		bpts1(l, p0.Mul(8), p01.Mul(4), p012.Mul(2), p0123, scale*8)
    58  		bpts1(l, p0123, p123.Mul(2), p23.Mul(4), p3.Mul(8), scale*8)
    59  	}
    60  }
    61  
    62  func bpts(l *[]Point, p0 Point, p1 Point, p2 Point, p3 Point) {
    63  	bpts1(l, p0, p1, p2, p3, 1)
    64  }
    65  
    66  func bezierpts(p0 Point, p1 Point, p2 Point, p3 Point) []Point {
    67  	var l []Point
    68  	bpts(&l, p0, p1, p2, p3)
    69  	appendpt(&l, p3)
    70  	return l
    71  }
    72  
    73  func _bezsplinepts(l *[]Point, pt []Point) {
    74  	if len(pt) < 3 {
    75  		return
    76  	}
    77  	ep := pt[len(pt)-3:]
    78  	periodic := pt[0] == ep[2]
    79  	var a, b, c, d Point
    80  	if periodic {
    81  		a = ep[1].Add(pt[0]).Div(2)
    82  		b = ep[1].Add(pt[0].Mul(5)).Div(6)
    83  		c = pt[0].Mul(5).Add(pt[1]).Div(6)
    84  		d = pt[0].Add(pt[1]).Div(2)
    85  		bpts(l, a, b, c, d)
    86  	}
    87  	for p := pt; len(p) >= len(ep); p = p[1:] {
    88  		if len(p) == len(pt) && !periodic {
    89  			a = p[0]
    90  			b = p[0].Add(p[1].Mul(2)).Div(3)
    91  		} else {
    92  			a = p[0].Add(p[1]).Div(2)
    93  			b = p[0].Add(p[1].Mul(5)).Div(6)
    94  		}
    95  		if len(p) == len(ep) && !periodic {
    96  			c = p[1].Mul(2).Add(p[2]).Div(3)
    97  			d = p[2]
    98  		} else {
    99  			c = p[1].Mul(5).Add(p[2]).Div(6)
   100  			d = p[1].Add(p[2]).Div(2)
   101  		}
   102  		bpts(l, a, b, c, d)
   103  	}
   104  	appendpt(l, d)
   105  }
   106  
   107  func bezsplinepts(pt []Point) []Point {
   108  	var l []Point
   109  	_bezsplinepts(&l, pt)
   110  	return l
   111  }
   112  
   113  // Bezier draws the cubic Bezier curve defined by Points a, b, c, and d.
   114  // The end styles are determined by end0 and end1; the thickness
   115  // of the curve is 1+2*thick.
   116  // The source is aligned so sp in src corresponds to a in dst.
   117  func (dst *Image) Bezier(a, b, c, d Point, end0, end1 End, radius int, src *Image, sp Point) {
   118  	dst.BezierOp(a, b, c, d, end0, end1, radius, src, sp, SoverD)
   119  }
   120  
   121  // BezierOp is like Bezier but specifies an explicit Porter-Duff operator.
   122  func (dst *Image) BezierOp(a, b, c, d Point, end0, end1 End, radius int, src *Image, sp Point, op Op) {
   123  	l := bezierpts(a, b, c, d)
   124  	if len(l) > 0 {
   125  		dst.PolyOp(l, end0, end1, radius, src, sp.Sub(a).Add(l[0]), op)
   126  	}
   127  }
   128  
   129  // BSpline takes the same arguments as Poly but draws a quadratic B-spline
   130  // rather than a polygon.  If the first and last points in p are equal,
   131  // the spline has periodic end conditions.
   132  func (dst *Image) BSpline(pt []Point, end0, end1 End, radius int, src *Image, sp Point) {
   133  	dst.BSplineOp(pt, end0, end1, radius, src, sp, SoverD)
   134  }
   135  
   136  // BSplineOp is like BSpline but specifies an explicit Porter-Duff operator.
   137  func (dst *Image) BSplineOp(pt []Point, end0, end1 End, radius int, src *Image, sp Point, op Op) {
   138  	var l []Point
   139  	_bezsplinepts(&l, pt)
   140  	if len(l) > 0 {
   141  		dst.PolyOp(l, end0, end1, radius, src, sp.Sub(pt[0]).Add(l[0]), op)
   142  	}
   143  }
   144  
   145  // FillBezier is like FillPoly but fills the cubic Bezier curve defined by a, b, c, d.
   146  func (dst *Image) FillBezier(a, b, c, d Point, wind int, src *Image, sp Point) {
   147  	dst.FillBezierOp(a, b, c, d, wind, src, sp, SoverD)
   148  }
   149  
   150  // FillBezierOp is like FillBezier but specifies an explicit Porter-Duff operator.
   151  func (dst *Image) FillBezierOp(a, b, c, d Point, wind int, src *Image, sp Point, op Op) {
   152  	l := bezierpts(a, b, c, d)
   153  	if len(l) > 0 {
   154  		dst.FillPolyOp(l, wind, src, sp.Sub(a).Add(l[0]), op)
   155  	}
   156  }
   157  
   158  // FillBSpline is like FillPoly but fills the quadratic B-spline defined by p,
   159  // not the polygon defined by p.
   160  // The spline is closed with a line if necessary.
   161  func (dst *Image) FillBSpline(pt []Point, wind int, src *Image, sp Point) {
   162  	dst.FillBSplineOp(pt, wind, src, sp, SoverD)
   163  }
   164  
   165  // FillBSplineOp is like FillBSpline but specifies an explicit Porter-Duff operator.
   166  func (dst *Image) FillBSplineOp(pt []Point, wind int, src *Image, sp Point, op Op) {
   167  	var l []Point
   168  	_bezsplinepts(&l, pt)
   169  	if len(l) > 0 {
   170  		dst.FillPolyOp(l, wind, src, sp.Sub(pt[0]).Add(l[0]), op)
   171  	}
   172  }