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  }