Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

94 wiersze
2.3KB

  1. package path
  2. import (
  3. "fmt"
  4. "git.wtrh.nl/patterns/gopatterns/pkg/pattern/point"
  5. "github.com/tdewolff/canvas"
  6. splines "gitlab.com/Achilleshiel/gosplines"
  7. )
  8. const resolution = 40
  9. // Spline defines a smooth curved path through points.
  10. type Spline struct {
  11. *Path
  12. start, end point.Point
  13. }
  14. type SplineOpts struct {
  15. Start, End point.Point
  16. Points []point.Point
  17. Style Style
  18. }
  19. // NewSpline returns a new spline through points. Start and end points can be provided as
  20. // the start and stop direction of the spline. When start or end point arguments are left nil there
  21. // are no constraints on the direction.
  22. func NewSpline(opts SplineOpts) Spline {
  23. s := Spline{
  24. Path: NewPath(opts.Points, opts.Style),
  25. start: opts.Start,
  26. end: opts.End,
  27. }
  28. if opts.Start == nil && len(opts.Points) > 1 {
  29. s.start = opts.Points[0]
  30. }
  31. if opts.End == nil && len(opts.Points) > 1 {
  32. s.end = opts.Points[len(opts.Points)-1]
  33. }
  34. return s
  35. }
  36. // Draw the spline to the provided [canvas.Canvas].
  37. func (s Spline) Draw(c *canvas.Canvas) error {
  38. if len(s.points) < 2 {
  39. return nil
  40. }
  41. x := make([]float64, len(s.points))
  42. y := make([]float64, len(s.points))
  43. for i, p := range s.points {
  44. x[i], y[i] = p.Vector().Values()
  45. }
  46. diffStart := s.start.Vector().Subtract(s.points[0].Vector())
  47. diffEnd := s.points[len(s.points)-1].Vector().Subtract(s.end.Vector())
  48. xCoefficient, err := splines.SolveSplineWithConstraint(x, diffStart.X, diffEnd.X)
  49. if err != nil {
  50. return fmt.Errorf("unable to calculate coefficients for x: %w", err)
  51. }
  52. yCoefficient, err := splines.SolveSplineWithConstraint(y, diffStart.Y, diffEnd.Y)
  53. if err != nil {
  54. return fmt.Errorf("unable to calculate coefficients for y: %w", err)
  55. }
  56. points := make([]point.Point, 0, len(x)*resolution)
  57. stepSize := 1.0 / float64(resolution-1)
  58. for i := range len(s.points) - 1 {
  59. points = append(points, s.points[i])
  60. for t := stepSize; t < 1.0; t += stepSize {
  61. xCalculated := xCoefficient[i].Calculate(t)
  62. yCalculated := yCoefficient[i].Calculate(t)
  63. points = append(points, point.NewAbsolutePoint(xCalculated, yCalculated, 0, "0"))
  64. }
  65. }
  66. points = append(points, s.points[len(s.points)-1])
  67. path := NewPath(points, s.style)
  68. if err = path.Draw(c); err != nil {
  69. return fmt.Errorf("draw spline points to canvas: %w", err)
  70. }
  71. return nil
  72. }