| @@ -42,6 +42,8 @@ linters-settings: | |||
| - github.com/tdewolff/canvas | |||
| - github.com/tdewolff/canvas/renderers | |||
| - gitlab.com/Achilleshiel/gosplines | |||
| - gitlab.com/slxh/go/env | |||
| gci: | |||
| sections: | |||
| @@ -4,12 +4,12 @@ import ( | |||
| _ "embed" | |||
| "flag" | |||
| "fmt" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/template" | |||
| "gitlab.com/slxh/go/env" | |||
| "log/slog" | |||
| "os" | |||
| gotemplate "text/template" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/template" | |||
| "gitlab.com/slxh/go/env" | |||
| ) | |||
| //go:embed combined_pdf.tex.template | |||
| @@ -54,6 +54,7 @@ gopatterns [-templates <template-dir>] [-out <output-dir>] input-file | |||
| } | |||
| files := make([]string, 0) | |||
| for _, arg := range args { | |||
| pattern, err := template.LoadPattern(arg) | |||
| if err != nil { | |||
| @@ -2,11 +2,11 @@ package pattern | |||
| import ( | |||
| "fmt" | |||
| "gopkg.in/yaml.v3" | |||
| "io/fs" | |||
| "math" | |||
| "gopkg.in/Knetic/govaluate.v3" | |||
| "gopkg.in/yaml.v3" | |||
| ) | |||
| // DimensionID describe the ID of the dimension. | |||
| @@ -3,6 +3,7 @@ package pattern | |||
| import ( | |||
| "fmt" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/point" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/text" | |||
| "github.com/tdewolff/canvas" | |||
| @@ -1,10 +1,11 @@ | |||
| package point | |||
| import ( | |||
| "math" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/position" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/vector" | |||
| "github.com/tdewolff/canvas" | |||
| "math" | |||
| ) | |||
| // ExtendPoint defines a point on the line between two other points. | |||
| @@ -74,6 +74,7 @@ func Draw(c *canvas.Canvas, point Point, face *canvas.FontFace, debug bool) { | |||
| StrokeJoiner: canvas.BevelJoin, | |||
| } | |||
| xLine := canvas.Line(10, 0) | |||
| c.RenderPath(yLine, yStyle, m) | |||
| c.RenderPath(xLine, xStyle, m) | |||
| } | |||
| @@ -3,12 +3,12 @@ package template | |||
| import ( | |||
| "errors" | |||
| "fmt" | |||
| "gopkg.in/Knetic/govaluate.v3" | |||
| "math" | |||
| "strconv" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/point" | |||
| "gopkg.in/Knetic/govaluate.v3" | |||
| ) | |||
| const maxRecursionDepth = 100 | |||
| @@ -71,6 +71,7 @@ func (p Points) Functions(pat *pattern.Pattern) map[string]govaluate.ExpressionF | |||
| func (p Points) getOrCreateFromArgs(pat *pattern.Pattern, args ...interface{}) ([]point.Point, error) { | |||
| points := make([]point.Point, 0, len(args)) | |||
| for i, arg := range args { | |||
| id, err := toPointID(arg) | |||
| if err != nil { | |||
| @@ -95,9 +96,12 @@ func toPointID(arg interface{}) (point.ID, error) { | |||
| if !ok { | |||
| return "", fmt.Errorf("parsing %v as PointID: %w", arg, ErrInvalidPointID) | |||
| } | |||
| v1 = strconv.FormatFloat(f, 'f', -1, 64) | |||
| } | |||
| id1 := point.ID(v1) | |||
| return id1, nil | |||
| } | |||
| @@ -116,15 +120,46 @@ func (p Points) evaluationFunctions() map[string]govaluate.ExpressionFunction { | |||
| ErrInvalidArguments) | |||
| } | |||
| return math.Acos(args[0].(float64)), nil | |||
| x, ok := args[0].(float64) | |||
| if !ok { | |||
| return nil, fmt.Errorf("evaluate acos(): parsing %q as float64: %w", args[0], | |||
| ErrInvalidArguments) | |||
| } | |||
| return math.Acos(x), nil | |||
| }, | |||
| "asin": func(args ...interface{}) (interface{}, error) { | |||
| if len(args) != 1 { | |||
| return nil, fmt.Errorf("function asin() requires 1 argument: %w", | |||
| ErrInvalidArguments) | |||
| } | |||
| x, ok := args[0].(float64) | |||
| if !ok { | |||
| return nil, fmt.Errorf("evaluate asin(): parsing %q as float64: %w", args[0], | |||
| ErrInvalidArguments) | |||
| } | |||
| return math.Asin(x), nil | |||
| }, | |||
| "atan2": func(args ...interface{}) (interface{}, error) { | |||
| if len(args) != 2 { | |||
| return nil, fmt.Errorf("function atan2() requires 2 arguments: %w", | |||
| ErrInvalidArguments) | |||
| } | |||
| x, ok := args[0].(float64) | |||
| if !ok { | |||
| return nil, fmt.Errorf("evaluate atan2(): parsing %q as float64: %w", args[0], | |||
| ErrInvalidArguments) | |||
| } | |||
| y, ok := args[1].(float64) | |||
| if !ok { | |||
| return nil, fmt.Errorf("evaluate atan2(): parsing %q as float64: %w", args[0], | |||
| ErrInvalidArguments) | |||
| } | |||
| return math.Atan2(args[0].(float64), args[1].(float64)), nil | |||
| return math.Atan2(x, y), nil | |||
| }, | |||
| } | |||
| } | |||
| @@ -220,7 +255,7 @@ func (p Points) createRelative( | |||
| MarkWith(id), nil | |||
| } | |||
| //nolint:ireturn | |||
| //nolint:ireturn,dupl | |||
| func (p Points) createBetween(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) { | |||
| newPoint, ok := p[id] | |||
| if !ok { | |||
| @@ -268,6 +303,7 @@ func (p Points) getOrCreate(id point.ID, pat *pattern.Pattern, depth int) (point | |||
| return createdPoint, nil | |||
| } | |||
| //nolint:ireturn,dupl | |||
| func (p Points) createExtend(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) { | |||
| newPoint, ok := p[id] | |||
| if !ok { | |||
| @@ -298,6 +334,7 @@ func (p Points) createExtend(id point.ID, pat *pattern.Pattern, depth int) (poin | |||
| return point.NewExtendPoint(fromPoint, toPoint, offset, id), nil | |||
| } | |||
| //nolint:ireturn | |||
| func (p Points) createPolar(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) { | |||
| templatePoint, ok := p[id] | |||
| if !ok { | |||
| @@ -323,18 +360,24 @@ func (p Points) createPolar(id point.ID, pat *pattern.Pattern, depth int) (point | |||
| WithXOffset(x).WithYOffset(y).MarkWith(id), nil | |||
| } | |||
| // ExtendPoint describes how to draw a new point that extends in line with two points. | |||
| type ExtendPoint struct { | |||
| From point.ID `yaml:"from"` | |||
| To point.ID `yaml:"to"` | |||
| Offset *Value `yaml:"offset"` | |||
| } | |||
| // PolarPoint describes how to draw a new point with a direction and a distance from the current | |||
| // position. | |||
| type PolarPoint struct { | |||
| Length *Value `yaml:"length"` | |||
| Rotation *Value `yaml:"rotation"` | |||
| } | |||
| func (p PolarPoint) evaluate(params govaluate.MapParameters, funcs map[string]govaluate.ExpressionFunction) (x, y float64, err error) { | |||
| func (p PolarPoint) evaluate( | |||
| params govaluate.MapParameters, | |||
| funcs map[string]govaluate.ExpressionFunction, | |||
| ) (x, y float64, err error) { | |||
| rotation, err := p.Rotation.Evaluate(params, funcs) | |||
| if err != nil { | |||
| return 0, 0, err | |||
| @@ -8,7 +8,10 @@ type Position struct { | |||
| Rotation *Value | |||
| } | |||
| func (p Position) evaluate(params govaluate.MapParameters, funcs map[string]govaluate.ExpressionFunction) (x, y, rotation float64, err error) { | |||
| func (p Position) evaluate( | |||
| params govaluate.MapParameters, | |||
| funcs map[string]govaluate.ExpressionFunction, | |||
| ) (x, y, rotation float64, err error) { | |||
| x, err = p.X.Evaluate(params, funcs) | |||
| if err != nil { | |||
| return 0, 0, 0, err | |||
| @@ -2,14 +2,15 @@ package template | |||
| import ( | |||
| "fmt" | |||
| "path/filepath" | |||
| "slices" | |||
| "strings" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern/text" | |||
| "github.com/stoewer/go-strcase" | |||
| "github.com/tdewolff/canvas" | |||
| "github.com/tdewolff/canvas/renderers" | |||
| "path/filepath" | |||
| "slices" | |||
| "strings" | |||
| ) | |||
| // RenderPatterns loads a [Request] from yaml file and renders the pattern to an SVG. | |||
| @@ -20,7 +21,11 @@ func (s Storage) RenderPatterns(request Request, outputDir string, debug bool) ( | |||
| } | |||
| filenames := make([]string, 0, len(template.Panels)) | |||
| dim, err := s.Dimensions(request.Sizes) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("load dimensions: %w", err) | |||
| } | |||
| renderer := Renderer{dimensions: dim, owner: request.Owner, pattern: request.Template} | |||
| @@ -47,8 +52,10 @@ func (s Storage) RenderPatterns(request Request, outputDir string, debug bool) ( | |||
| c.Fit(10) | |||
| filename := filepath.Join(outputDir, strings.Join([]string{request.Template, name, | |||
| strcase.SnakeCase(request.Owner)}, "_")+".pdf") | |||
| filename := filepath.Join(outputDir, strings.Join([]string{ | |||
| request.Template, name, | |||
| strcase.SnakeCase(request.Owner), | |||
| }, "_")+".pdf") | |||
| filenames = append(filenames, filename) | |||
| @@ -57,6 +64,7 @@ func (s Storage) RenderPatterns(request Request, outputDir string, debug bool) ( | |||
| return nil, fmt.Errorf("write canvas to file: %w", err) | |||
| } | |||
| } | |||
| return filenames, nil | |||
| } | |||
| @@ -111,5 +119,6 @@ func (r Renderer) GenerateInformation(p Panel, pat *pattern.Pattern) error { | |||
| point := pat.GetPoint("_information") | |||
| point.SetHide() | |||
| pat.AddText(text.NewText(point, "", strings.Join(dimensions, "\n"))) | |||
| return nil | |||
| } | |||
| @@ -2,10 +2,11 @@ package template | |||
| import ( | |||
| "fmt" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern" | |||
| "gopkg.in/yaml.v3" | |||
| "io/fs" | |||
| "os" | |||
| "git.wtrh.nl/patterns/gopatterns/pkg/pattern" | |||
| "gopkg.in/yaml.v3" | |||
| ) | |||
| type Storage struct { | |||
| @@ -14,7 +14,9 @@ var ErrNonFloatValue = errors.New("failed to cast expression result for float64" | |||
| type Value string | |||
| // Evaluate a Value as [govaluate.EvaluateExpression] in combination with the provided parameters. | |||
| func (v *Value) Evaluate(parameters govaluate.MapParameters, funcs map[string]govaluate.ExpressionFunction) (float64, error) { | |||
| func (v *Value) Evaluate(parameters govaluate.MapParameters, | |||
| funcs map[string]govaluate.ExpressionFunction, | |||
| ) (float64, error) { | |||
| if v == nil { | |||
| return 0, nil | |||
| } | |||