| @@ -6,10 +6,9 @@ linters: | |||
| - nlreturn # covered by wsl cuddle rules | |||
| - nonamedreturns # named returns are accepted | |||
| - mnd | |||
| - gomnd | |||
| # deprecated | |||
| - exhaustruct | |||
| - execinquery | |||
| - tenv | |||
| severity: | |||
| default-severity: major | |||
| @@ -2,33 +2,25 @@ | |||
| package path | |||
| import ( | |||
| "image/color" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/point" | |||
| "github.com/tdewolff/canvas" | |||
| ) | |||
| // Path defines a set of straight lines through points. | |||
| type Path struct { | |||
| points []point.Point | |||
| thickness float64 | |||
| color color.RGBA | |||
| points []point.Point | |||
| style Style | |||
| } | |||
| // NewPath returns a new [Path]. | |||
| func NewPath(points ...point.Point) *Path { | |||
| black := canvas.Black | |||
| return &Path{points: points, color: black, thickness: 0.2} | |||
| } | |||
| // NewPathWithStyle returns a new [Path] with the specified thickness and color. | |||
| func NewPathWithStyle(thickness float64, color color.RGBA, points ...point.Point) Path { | |||
| return Path{points: points, color: color, thickness: thickness} | |||
| func NewPath(points []point.Point, style Style) *Path { | |||
| return &Path{points: points, style: style} | |||
| } | |||
| // SetThickness updates the tickness of the line | |||
| func (p *Path) SetThickness(thickness float64) { | |||
| p.thickness = thickness | |||
| // WithStyle updates the style of the Path. | |||
| func (p *Path) WithStyle(Style Style) *Path { | |||
| p.style = Style | |||
| return p | |||
| } | |||
| // Draw the path to the provided [canvas.Canvas]. | |||
| @@ -38,17 +30,7 @@ func (p *Path) Draw(c *canvas.Canvas) error { | |||
| polyline.Add(next.Vector().Values()) | |||
| } | |||
| c.RenderPath(polyline.ToPath(), | |||
| canvas.Style{ | |||
| Fill: canvas.Paint{}, | |||
| Stroke: canvas.Paint{Color: p.color}, | |||
| StrokeWidth: p.thickness, | |||
| StrokeCapper: canvas.RoundCap, | |||
| StrokeJoiner: canvas.BevelJoin, | |||
| DashOffset: 0, | |||
| Dashes: nil, | |||
| FillRule: 0, | |||
| }, canvas.Identity) | |||
| c.RenderPath(polyline.ToPath(), p.style.ToCanvas(), canvas.Identity) | |||
| return nil | |||
| } | |||
| @@ -2,7 +2,6 @@ package path | |||
| import ( | |||
| "fmt" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/point" | |||
| "github.com/tdewolff/canvas" | |||
| splines "gitlab.com/Achilleshiel/gosplines" | |||
| @@ -16,42 +15,48 @@ type Spline struct { | |||
| start, end point.Point | |||
| } | |||
| type SplineOpts struct { | |||
| Start, End point.Point | |||
| Points []point.Point | |||
| Style Style | |||
| } | |||
| // NewSpline returns a new spline through points. Start and end points can be provided as | |||
| // the start and stop direction of the spline. When start or end point arguments are left nil there | |||
| // are no constraints on the direction. | |||
| func NewSpline(start, end point.Point, points ...point.Point) Spline { | |||
| func NewSpline(opts SplineOpts) Spline { | |||
| s := Spline{ | |||
| Path: NewPath(points...), | |||
| start: start, | |||
| end: end, | |||
| Path: NewPath(opts.Points, opts.Style), | |||
| start: opts.Start, | |||
| end: opts.End, | |||
| } | |||
| if start == nil && len(points) > 1 { | |||
| s.start = points[0] | |||
| if opts.Start == nil && len(opts.Points) > 1 { | |||
| s.start = opts.Points[0] | |||
| } | |||
| if end == nil && len(points) > 1 { | |||
| s.end = points[len(points)-1] | |||
| if opts.End == nil && len(opts.Points) > 1 { | |||
| s.end = opts.Points[len(opts.Points)-1] | |||
| } | |||
| return s | |||
| } | |||
| // Draw the spline to the provided [canvas.Canvas]. | |||
| func (p Spline) Draw(c *canvas.Canvas) error { | |||
| if len(p.points) < 2 { | |||
| func (s Spline) Draw(c *canvas.Canvas) error { | |||
| if len(s.points) < 2 { | |||
| return nil | |||
| } | |||
| x := make([]float64, len(p.points)) | |||
| y := make([]float64, len(p.points)) | |||
| x := make([]float64, len(s.points)) | |||
| y := make([]float64, len(s.points)) | |||
| for i, point := range p.points { | |||
| x[i], y[i] = point.Vector().Values() | |||
| for i, p := range s.points { | |||
| x[i], y[i] = p.Vector().Values() | |||
| } | |||
| diffStart := p.start.Vector().Subtract(p.points[0].Vector()) | |||
| diffEnd := p.points[len(p.points)-1].Vector().Subtract(p.end.Vector()) | |||
| diffStart := s.start.Vector().Subtract(s.points[0].Vector()) | |||
| diffEnd := s.points[len(s.points)-1].Vector().Subtract(s.end.Vector()) | |||
| xCoefficient, err := splines.SolveSplineWithConstraint(x, diffStart.X, diffEnd.X) | |||
| if err != nil { | |||
| @@ -66,8 +71,8 @@ func (p Spline) Draw(c *canvas.Canvas) error { | |||
| points := make([]point.Point, 0, len(x)*resolution) | |||
| stepSize := 1.0 / float64(resolution-1) | |||
| for i := range len(p.points) - 1 { | |||
| points = append(points, p.points[i]) | |||
| for i := range len(s.points) - 1 { | |||
| points = append(points, s.points[i]) | |||
| for t := stepSize; t < 1.0; t += stepSize { | |||
| xCalculated := xCoefficient[i].Calculate(t) | |||
| @@ -76,10 +81,9 @@ func (p Spline) Draw(c *canvas.Canvas) error { | |||
| } | |||
| } | |||
| points = append(points, p.points[len(p.points)-1]) | |||
| points = append(points, s.points[len(s.points)-1]) | |||
| path := NewPath(points...) | |||
| path.SetThickness(p.thickness) | |||
| path := NewPath(points, s.style) | |||
| if err = path.Draw(c); err != nil { | |||
| return fmt.Errorf("draw spline points to canvas: %w", err) | |||
| @@ -0,0 +1,33 @@ | |||
| package path | |||
| import ( | |||
| "image/color" | |||
| "github.com/tdewolff/canvas" | |||
| ) | |||
| type Style struct { | |||
| Thickness float64 | |||
| Color color.RGBA | |||
| Dashes []float64 | |||
| } | |||
| func (s Style) ToCanvas() canvas.Style { | |||
| return canvas.Style{ | |||
| Fill: canvas.Paint{}, | |||
| Stroke: canvas.Paint{Color: s.Color}, | |||
| StrokeWidth: s.Thickness, | |||
| StrokeCapper: canvas.RoundCap, | |||
| StrokeJoiner: canvas.BevelJoin, | |||
| DashOffset: 0, | |||
| Dashes: s.Dashes, | |||
| FillRule: 0, | |||
| } | |||
| } | |||
| func NewDefaultStyle() Style { | |||
| return Style{ | |||
| Thickness: 0.2, | |||
| Color: canvas.Black, | |||
| } | |||
| } | |||
| @@ -11,7 +11,7 @@ import ( | |||
| "gopkg.in/Knetic/govaluate.v3" | |||
| ) | |||
| // Pattern contains all the points, lines and dimensions to draw a pattern to a canvas. | |||
| // The Pattern contains all the points, lines and dimensions to draw a pattern to a canvas. | |||
| type Pattern struct { | |||
| points map[point.ID]point.Point | |||
| lines []pathDrawer | |||
| @@ -34,7 +34,7 @@ func (p *Pattern) AddLine(line pathDrawer) { | |||
| } | |||
| // GetPoints returns a slice with points for the given IDs. | |||
| func (p *Pattern) GetPoints(id ...point.ID) []point.Point { | |||
| func (p *Pattern) GetPoints(id []point.ID) []point.Point { | |||
| points := make([]point.Point, 0, len(id)) | |||
| for _, i := range id { | |||
| points = append(points, p.GetPoint(i)) | |||
| @@ -28,29 +28,29 @@ type Curve struct { | |||
| // Build adds the line to the provided [pattern.Pattern]. | |||
| func (l Line) Build(pat *pattern.Pattern) error { | |||
| points := pat.GetPoints(l.Through...) | |||
| points := pat.GetPoints(l.Through) | |||
| for _, p := range points { | |||
| p.SetDraw() | |||
| } | |||
| switch { | |||
| case l.Curve != nil: | |||
| startPoint := pat.GetPoint(l.Curve.Start) | |||
| endPoint := pat.GetPoint(l.Curve.End) | |||
| spline := path.NewSpline(startPoint, endPoint, points...) | |||
| style := path.NewDefaultStyle() | |||
| if l.Style != nil && l.Style.Thickness != nil { | |||
| spline.SetThickness(*l.Style.Thickness) | |||
| } | |||
| if l.Style != nil && l.Style.Thickness != nil { | |||
| style.Thickness = *l.Style.Thickness | |||
| } | |||
| pat.AddLine(spline) | |||
| switch { | |||
| case l.Curve != nil: | |||
| pat.AddLine( | |||
| path.NewSpline(path.SplineOpts{ | |||
| Start: pat.GetPoint(l.Curve.Start), | |||
| End: pat.GetPoint(l.Curve.End), | |||
| Points: points, | |||
| Style: style, | |||
| }), | |||
| ) | |||
| default: | |||
| newPath := path.NewPath(points...) | |||
| if l.Style != nil && l.Style.Thickness != nil { | |||
| newPath.SetThickness(*l.Style.Thickness) | |||
| } | |||
| pat.AddLine(newPath) | |||
| pat.AddLine(path.NewPath(points, style)) | |||
| } | |||
| return nil | |||