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