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 }