mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
163 lines
3.6 KiB
Go
163 lines
3.6 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package draw
|
|
|
|
import (
|
|
"math"
|
|
|
|
"github.com/unidoc/unipdf/v3/model"
|
|
)
|
|
|
|
// CubicBezierCurve is defined by:
|
|
// R(t) = P0*(1-t)^3 + P1*3*t*(1-t)^2 + P2*3*t^2*(1-t) + P3*t^3
|
|
// where P0 is the current point, P1, P2 control points and P3 the final point.
|
|
type CubicBezierCurve struct {
|
|
P0 Point // Starting point.
|
|
P1 Point // Control point 1.
|
|
P2 Point // Control point 2.
|
|
P3 Point // Final point.
|
|
}
|
|
|
|
// NewCubicBezierCurve returns a new cubic Bezier curve.
|
|
func NewCubicBezierCurve(x0, y0, x1, y1, x2, y2, x3, y3 float64) CubicBezierCurve {
|
|
curve := CubicBezierCurve{}
|
|
curve.P0 = NewPoint(x0, y0)
|
|
curve.P1 = NewPoint(x1, y1)
|
|
curve.P2 = NewPoint(x2, y2)
|
|
curve.P3 = NewPoint(x3, y3)
|
|
return curve
|
|
}
|
|
|
|
// AddOffsetXY adds X,Y offset to all points on a curve.
|
|
func (curve CubicBezierCurve) AddOffsetXY(offX, offY float64) CubicBezierCurve {
|
|
curve.P0.X += offX
|
|
curve.P1.X += offX
|
|
curve.P2.X += offX
|
|
curve.P3.X += offX
|
|
|
|
curve.P0.Y += offY
|
|
curve.P1.Y += offY
|
|
curve.P2.Y += offY
|
|
curve.P3.Y += offY
|
|
|
|
return curve
|
|
}
|
|
|
|
// GetBounds returns the bounding box of the Bezier curve.
|
|
func (curve CubicBezierCurve) GetBounds() model.PdfRectangle {
|
|
minX := curve.P0.X
|
|
maxX := curve.P0.X
|
|
minY := curve.P0.Y
|
|
maxY := curve.P0.Y
|
|
|
|
// 1000 points.
|
|
for t := 0.0; t <= 1.0; t += 0.001 {
|
|
Rx := curve.P0.X*math.Pow(1-t, 3) +
|
|
curve.P1.X*3*t*math.Pow(1-t, 2) +
|
|
curve.P2.X*3*math.Pow(t, 2)*(1-t) +
|
|
curve.P3.X*math.Pow(t, 3)
|
|
Ry := curve.P0.Y*math.Pow(1-t, 3) +
|
|
curve.P1.Y*3*t*math.Pow(1-t, 2) +
|
|
curve.P2.Y*3*math.Pow(t, 2)*(1-t) +
|
|
curve.P3.Y*math.Pow(t, 3)
|
|
|
|
if Rx < minX {
|
|
minX = Rx
|
|
}
|
|
if Rx > maxX {
|
|
maxX = Rx
|
|
}
|
|
if Ry < minY {
|
|
minY = Ry
|
|
}
|
|
if Ry > maxY {
|
|
maxY = Ry
|
|
}
|
|
}
|
|
|
|
bounds := model.PdfRectangle{}
|
|
bounds.Llx = minX
|
|
bounds.Lly = minY
|
|
bounds.Urx = maxX
|
|
bounds.Ury = maxY
|
|
return bounds
|
|
}
|
|
|
|
// CubicBezierPath represents a collection of cubic Bezier curves.
|
|
type CubicBezierPath struct {
|
|
Curves []CubicBezierCurve
|
|
}
|
|
|
|
// NewCubicBezierPath returns a new empty cubic Bezier path.
|
|
func NewCubicBezierPath() CubicBezierPath {
|
|
bpath := CubicBezierPath{}
|
|
bpath.Curves = []CubicBezierCurve{}
|
|
return bpath
|
|
}
|
|
|
|
// AppendCurve appends the specified Bezier curve to the path.
|
|
func (p CubicBezierPath) AppendCurve(curve CubicBezierCurve) CubicBezierPath {
|
|
p.Curves = append(p.Curves, curve)
|
|
return p
|
|
}
|
|
|
|
// Copy returns a clone of the Bezier path.
|
|
func (p CubicBezierPath) Copy() CubicBezierPath {
|
|
bpathcopy := CubicBezierPath{}
|
|
bpathcopy.Curves = []CubicBezierCurve{}
|
|
for _, c := range p.Curves {
|
|
bpathcopy.Curves = append(bpathcopy.Curves, c)
|
|
}
|
|
return bpathcopy
|
|
}
|
|
|
|
// Offset shifts the Bezier path with the specified offsets.
|
|
func (p CubicBezierPath) Offset(offX, offY float64) CubicBezierPath {
|
|
for i, c := range p.Curves {
|
|
p.Curves[i] = c.AddOffsetXY(offX, offY)
|
|
}
|
|
return p
|
|
}
|
|
|
|
// GetBoundingBox returns the bounding box of the Bezier path.
|
|
func (p CubicBezierPath) GetBoundingBox() Rectangle {
|
|
bbox := Rectangle{}
|
|
|
|
minX := 0.0
|
|
maxX := 0.0
|
|
minY := 0.0
|
|
maxY := 0.0
|
|
for idx, c := range p.Curves {
|
|
curveBounds := c.GetBounds()
|
|
if idx == 0 {
|
|
minX = curveBounds.Llx
|
|
maxX = curveBounds.Urx
|
|
minY = curveBounds.Lly
|
|
maxY = curveBounds.Ury
|
|
continue
|
|
}
|
|
|
|
if curveBounds.Llx < minX {
|
|
minX = curveBounds.Llx
|
|
}
|
|
if curveBounds.Urx > maxX {
|
|
maxX = curveBounds.Urx
|
|
}
|
|
if curveBounds.Lly < minY {
|
|
minY = curveBounds.Lly
|
|
}
|
|
if curveBounds.Ury > maxY {
|
|
maxY = curveBounds.Ury
|
|
}
|
|
}
|
|
|
|
bbox.X = minX
|
|
bbox.Y = minY
|
|
bbox.Width = maxX - minX
|
|
bbox.Height = maxY - minY
|
|
return bbox
|
|
}
|