github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/contentstream/draw/bezier_curve.go (about) 1 /* 2 * This file is subject to the terms and conditions defined in 3 * file 'LICENSE.md', which is part of this source code package. 4 */ 5 6 package draw 7 8 import ( 9 "math" 10 11 "github.com/unidoc/unidoc/pdf/model" 12 ) 13 14 // Cubic bezier curves are defined by: 15 // R(t) = P0*(1-t)^3 + P1*3*t*(1-t)^2 + P2*3*t^2*(1-t) + P3*t^3 16 // where P0 is the current point, P1, P2 control points and P3 the final point. 17 type CubicBezierCurve struct { 18 P0 Point // Starting point. 19 P1 Point // Control point 1. 20 P2 Point // Control point 2. 21 P3 Point // Final point. 22 } 23 24 func NewCubicBezierCurve(x0, y0, x1, y1, x2, y2, x3, y3 float64) CubicBezierCurve { 25 curve := CubicBezierCurve{} 26 curve.P0 = NewPoint(x0, y0) 27 curve.P1 = NewPoint(x1, y1) 28 curve.P2 = NewPoint(x2, y2) 29 curve.P3 = NewPoint(x3, y3) 30 return curve 31 } 32 33 // Add X,Y offset to all points on a curve. 34 func (curve CubicBezierCurve) AddOffsetXY(offX, offY float64) CubicBezierCurve { 35 curve.P0.X += offX 36 curve.P1.X += offX 37 curve.P2.X += offX 38 curve.P3.X += offX 39 40 curve.P0.Y += offY 41 curve.P1.Y += offY 42 curve.P2.Y += offY 43 curve.P3.Y += offY 44 45 return curve 46 } 47 48 func (curve CubicBezierCurve) GetBounds() model.PdfRectangle { 49 minX := curve.P0.X 50 maxX := curve.P0.X 51 minY := curve.P0.Y 52 maxY := curve.P0.Y 53 54 // 1000 points. 55 for t := 0.0; t <= 1.0; t += 0.001 { 56 Rx := curve.P0.X*math.Pow(1-t, 3) + 57 curve.P1.X*3*t*math.Pow(1-t, 2) + 58 curve.P2.X*3*math.Pow(t, 2)*(1-t) + 59 curve.P3.X*math.Pow(t, 3) 60 Ry := curve.P0.Y*math.Pow(1-t, 3) + 61 curve.P1.Y*3*t*math.Pow(1-t, 2) + 62 curve.P2.Y*3*math.Pow(t, 2)*(1-t) + 63 curve.P3.Y*math.Pow(t, 3) 64 65 if Rx < minX { 66 minX = Rx 67 } 68 if Rx > maxX { 69 maxX = Rx 70 } 71 if Ry < minY { 72 minY = Ry 73 } 74 if Ry > maxY { 75 maxY = Ry 76 } 77 } 78 79 bounds := model.PdfRectangle{} 80 bounds.Llx = minX 81 bounds.Lly = minY 82 bounds.Urx = maxX 83 bounds.Ury = maxY 84 return bounds 85 } 86 87 type CubicBezierPath struct { 88 Curves []CubicBezierCurve 89 } 90 91 func NewCubicBezierPath() CubicBezierPath { 92 bpath := CubicBezierPath{} 93 bpath.Curves = []CubicBezierCurve{} 94 return bpath 95 } 96 97 func (this CubicBezierPath) AppendCurve(curve CubicBezierCurve) CubicBezierPath { 98 this.Curves = append(this.Curves, curve) 99 return this 100 } 101 102 func (bpath CubicBezierPath) Copy() CubicBezierPath { 103 bpathcopy := CubicBezierPath{} 104 bpathcopy.Curves = []CubicBezierCurve{} 105 for _, c := range bpath.Curves { 106 bpathcopy.Curves = append(bpathcopy.Curves, c) 107 } 108 return bpathcopy 109 } 110 111 func (bpath CubicBezierPath) Offset(offX, offY float64) CubicBezierPath { 112 for i, c := range bpath.Curves { 113 bpath.Curves[i] = c.AddOffsetXY(offX, offY) 114 } 115 return bpath 116 } 117 118 func (bpath CubicBezierPath) GetBoundingBox() Rectangle { 119 bbox := Rectangle{} 120 121 minX := 0.0 122 maxX := 0.0 123 minY := 0.0 124 maxY := 0.0 125 for idx, c := range bpath.Curves { 126 curveBounds := c.GetBounds() 127 if idx == 0 { 128 minX = curveBounds.Llx 129 maxX = curveBounds.Urx 130 minY = curveBounds.Lly 131 maxY = curveBounds.Ury 132 continue 133 } 134 135 if curveBounds.Llx < minX { 136 minX = curveBounds.Llx 137 } 138 if curveBounds.Urx > maxX { 139 maxX = curveBounds.Urx 140 } 141 if curveBounds.Lly < minY { 142 minY = curveBounds.Lly 143 } 144 if curveBounds.Ury > maxY { 145 maxY = curveBounds.Ury 146 } 147 } 148 149 bbox.X = minX 150 bbox.Y = minY 151 bbox.Width = maxX - minX 152 bbox.Height = maxY - minY 153 return bbox 154 }